/[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.86 by wakaba, Thu Mar 6 15:56:52 2008 UTC revision 1.207 by wakaba, Mon Oct 13 08:27:44 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)  require IO::Handle;
23  ## TODO: 1252 parse error (revision 1264)  
24  ## TODO: 8859-11 = 874 (revision 1271)  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  my $permitted_slash_tag_name = {  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    base => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    link => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    meta => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    hr => 1,  
31    br => 1,  ## Bits 12-15
32    img => 1,  sub SPECIAL_EL () { 0b1_000000000000000 }
33    embed => 1,  sub SCOPING_EL () { 0b1_00000000000000 }
34    param => 1,  sub FORMATTING_EL () { 0b1_0000000000000 }
35    area => 1,  sub PHRASING_EL () { 0b1_000000000000 }
36    col => 1,  
37    input => 1,  ## Bits 10-11
38    sub FOREIGN_EL () { 0b1_00000000000 }
39    sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
40    
41    ## Bits 6-9
42    sub TABLE_SCOPING_EL () { 0b1_000000000 }
43    sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
44    sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
45    sub TABLE_ROWS_EL () { 0b1_000000 }
46    
47    ## Bit 5
48    sub ADDRESS_DIV_P_EL () { 0b1_00000 }
49    
50    ## NOTE: Used in </body> and EOF algorithms.
51    ## Bit 4
52    sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
53    
54    ## NOTE: Used in "generate implied end tags" algorithm.
55    ## NOTE: There is a code where a modified version of
56    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
57    ## implementation (search for the algorithm name).
58    ## Bit 3
59    sub END_TAG_OPTIONAL_EL () { 0b1_000 }
60    
61    ## Bits 0-2
62    
63    sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
64    sub FORM_EL () { SPECIAL_EL | 0b001 }
65    sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
66    sub HEADING_EL () { SPECIAL_EL | 0b011 }
67    sub SELECT_EL () { SPECIAL_EL | 0b100 }
68    sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
69    
70    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
71    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
72    
73    sub 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 $c1_entity_char = {  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    ## 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 59  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).
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
391    
392  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
393      my $self = shift;
394      my $charset_name = shift;
395      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
396      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
397    } # parse_byte_string
398    
399    sub parse_byte_stream ($$$$;$$) {
400      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
401    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
402    my $charset = shift;    my $charset_name = shift;
403    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
   my $s;  
     
   if (defined $charset) {  
     require Encode; ## TODO: decode(utf8) don't delete BOM  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = lc $charset; ## TODO: normalize name  
     $self->{confident} = 1;  
   } else {  
     ## TODO: Implement HTML5 detection algorithm  
     require Whatpm::Charset::UniversalCharDet;  
     $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string  
         (substr ($$bytes_s, 0, 1024));  
     $charset ||= 'windows-1252';  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = $charset;  
     $self->{confident} = 0;  
   }  
404    
405    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
406      my $self = shift;      my (%opt) = @_;
407      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
408      ## TODO: if $charset is supported    };
409      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
410    
411      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
412        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
413      ## Step 1        };
414      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
415        $charset = 'utf-8';    ## 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      ## Step 2
451      if (defined $self->{input_encoding} and      my $byte_buffer = '';
452          $self->{input_encoding} eq $charset) {      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;        $self->{confident} = 1;
465        return;        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      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
483          ':'.$charset, level => 'w');      ## TODO: <meta charset>
484    
485      ## Step 3      ## Step 5
486      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
487    
488      ## Step 4      ## Step 6
489      throw Whatpm::HTML::RestartParser (charset => $charset);      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          !!!parse-error (type => 'charset label detected',
588                          text => $self->{input_encoding},
589                          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}    }; # $self->{change_encoding}
604    
605    my @args = @_; shift @args; # $s    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;    my $return;
620    try {    try {
621      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
622    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
623      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
624      $s = \ (Encode::decode ($charset, $$bytes_s));      
625      $self->{input_encoding} = $charset; ## TODO: normalize      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;      $self->{confident} = 1;
644      $return = $self->parse_char_string ($s, @args);  
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;    return $return;
651  } # parse_byte_string  } # parse_byte_stream
652    
653  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## 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  ## and the HTML layer MUST ignore it.  However, we does strip BOM in
# Line 162  sub parse_byte_string ($$$$;$) { Line 659  sub parse_byte_string ($$$$;$) {
659  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
660  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
661    
662  *parse_char_string = \&parse_string;  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_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
673    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
674    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
675    $self->{document} = $_[1];    $self->{document} = $_[1];
676    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
677    
# Line 175  sub parse_string ($$$;$) { Line 680  sub parse_string ($$$;$) {
680    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
681    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
682        if defined $self->{input_encoding};        if defined $self->{input_encoding};
683    ## TODO: |{input_encoding}| is needless?
684    
685    my $i = 0;    $self->{line_prev} = $self->{line} = 1;
686    my $line = 1;    $self->{column_prev} = -1;
687    my $column = 0;    $self->{column} = 0;
688    $self->{set_next_char} = sub {    $self->{set_nc} = sub {
689      my $self = shift;      my $self = shift;
690    
691      pop @{$self->{prev_char}};      my $char = '';
692      unshift @{$self->{prev_char}}, $self->{next_char};      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      $self->{next_char} = -1 and return if $i >= length $$s;        if ($input->read ($char, 1)) {
712      $self->{next_char} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
713      $column++;        } else {
714            $self->{nc} = -1;
715            return;
716          }
717        }
718    
719        ($self->{line_prev}, $self->{column_prev})
720            = ($self->{line}, $self->{column});
721        $self->{column}++;
722            
723      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
724        $line++;        !!!cp ('j1');
725        $column = 0;        $self->{line}++;
726      } elsif ($self->{next_char} == 0x000D) { # CR        $self->{column} = 0;
727        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
728        $self->{next_char} = 0x000A; # LF # MUST        !!!cp ('j2');
729        $line++;  ## TODO: support for abort/streaming
730        $column = 0;        my $next = '';
731      } elsif ($self->{next_char} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
732        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
733      } elsif ($self->{next_char} == 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_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
741      }      }
742    };    };
743    $self->{prev_char} = [-1, -1, -1];  
744    $self->{next_char} = -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_char} = sub {      level => {must => 'm',
818      $self->{next_char} = -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      #      #
# Line 254  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 848  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
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 }  sub DATA_STATE () { 0 }
851  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
852  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
853  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
854  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 265  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 859  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
859  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
860  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
861  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
862  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
863  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
864  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
865  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 287  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 881  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
881  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
882  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
883  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  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 }
# Line 303  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.  ## NOTE: "initial" and "before html" insertion modes have no constants.
931    
# Line 325  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
# Line 333  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 957  sub IN_COLUMN_GROUP_IM () { 0b10 }
957  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
958    my $self = shift;    my $self = shift;
959    $self->{state} = DATA_STATE; # 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_char}    $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 350  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  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{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}  ##        ->{name}
988  ##        ->{value}  ##        ->{value}
989  ##        ->{has_reference} == 1 or 0  ##        ->{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 367  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  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1004  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
1005  ## contains some requirements that are not detected by the  
1006  ## parsing algorithm:  my $is_space = {
1007  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1008  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1009  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1010  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1011  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1012  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1013    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
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} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1033        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1034    
1035          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          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1077              not $self->{escape}) {              not $self->{escape}) {
1078            !!!cp (1);            !!!cp (1);
1079            $self->{state} = ENTITY_DATA_STATE;            ## 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);            !!!cp (2);
1090            #            #
1091          }          }
1092        } elsif ($self->{next_char} == 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_char}->[0] == 0x002D and # -            
1096                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1097                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1098                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1099                $self->{escape} = 1;              $self->{s_kwd} = '--';
1100              } else {              #
1101                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1102              }              !!!cp (4);
1103                $self->{s_kwd} = '--';
1104                #
1105            } else {            } else {
1106              !!!cp (5);              !!!cp (5);
1107                #
1108            }            }
1109          }          }
1110                    
1111          #          #
1112        } elsif ($self->{next_char} == 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})) {
# Line 428  sub _get_next_token ($) { Line 1130  sub _get_next_token ($) {
1130            redo A;            redo A;
1131          } else {          } else {
1132            !!!cp (7);            !!!cp (7);
1133              $self->{s_kwd} = '';
1134            #            #
1135          }          }
1136        } elsif ($self->{next_char} == 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_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1140              !!!cp (8);              !!!cp (8);
1141              delete $self->{escape};              delete $self->{escape};
1142            } else {            } else {
# Line 444  sub _get_next_token ($) { Line 1146  sub _get_next_token ($) {
1146            !!!cp (10);            !!!cp (10);
1147          }          }
1148                    
1149            $self->{s_kwd} = '';
1150          #          #
1151        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1152          !!!cp (11);          !!!cp (11);
1153          !!!emit ({type => END_OF_FILE_TOKEN});          $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 {        } else {
1158          !!!cp (12);          !!!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_char}};                     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} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
1172    
1173        unless (defined $token) {        ## Stay in the data state.
1174          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1175          !!!cp (13);          !!!cp (13);
1176          !!!emit ({type => CHARACTER_TOKEN, data => '&'});          $self->{state} = PCDATA_STATE;
1177        } else {        } else {
1178          !!!cp (14);          !!!cp (14);
1179          !!!emit ($token);          ## Stay in the state.
1180        }        }
1181          !!!next-input-character;
1182          !!!emit ($token);
1183        redo A;        redo A;
1184      } elsif ($self->{state} == TAG_OPEN_STATE) {      } 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_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1187            !!!cp (15);            !!!cp (15);
1188            !!!next-input-character;            !!!next-input-character;
1189            $self->{state} = CLOSE_TAG_OPEN_STATE;            $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            !!!cp (16);            !!!cp (16);
1197            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!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_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1209            !!!cp (17);            !!!cp (17);
1210            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1211            !!!next-input-character;            !!!next-input-character;
1212            redo A;            redo A;
1213          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1214            !!!cp (18);            !!!cp (18);
1215            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1216            !!!next-input-character;            !!!next-input-character;
1217            redo A;            redo A;
1218          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1219                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1220            !!!cp (19);            !!!cp (19);
1221            $self->{current_token}            $self->{ct}
1222              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1223                 tag_name => chr ($self->{next_char} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
1224                   line => $self->{line_prev},
1225                   column => $self->{column_prev}};
1226            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1227            !!!next-input-character;            !!!next-input-character;
1228            redo A;            redo A;
1229          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1230                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1231            !!!cp (20);            !!!cp (20);
1232            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1233                              tag_name => chr ($self->{next_char})};                                      tag_name => chr ($self->{nc}),
1234                                        line => $self->{line_prev},
1235                                        column => $self->{column_prev}};
1236            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238            redo A;            redo A;
1239          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1240            !!!cp (21);            !!!cp (21);
1241            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag',
1242                              line => $self->{line_prev},
1243                              column => $self->{column_prev});
1244            $self->{state} = DATA_STATE;            $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_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1254            !!!cp (22);            !!!cp (22);
1255            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio',
1256                              line => $self->{line_prev},
1257                              column => $self->{column_prev});
1258            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1259            ## $self->{next_char} is intentionally left as is            $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            !!!cp (23);            !!!cp (23);
1267            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago',
1268                              line => $self->{line_prev},
1269                              column => $self->{column_prev});
1270            $self->{state} = DATA_STATE;            $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          }          }
# Line 551  sub _get_next_token ($) { Line 1281  sub _get_next_token ($) {
1281          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1282        }        }
1283      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } 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_char};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_char} == $c or $self->{next_char} == $C) {  
               !!!cp (24);  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               !!!cp (25);  
               $self->{next_char} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
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_char};            ## Reconsume.
1293                    redo A;
           unless ($self->{next_char} == 0x0009 or # HT  
                   $self->{next_char} == 0x000A or # LF  
                   $self->{next_char} == 0x000B or # VT  
                   $self->{next_char} == 0x000C or # FF  
                   $self->{next_char} == 0x0020 or # SP  
                   $self->{next_char} == 0x003E or # >  
                   $self->{next_char} == 0x002F or # /  
                   $self->{next_char} == -1) {  
             !!!cp (26);  
             $self->{next_char} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</'});  
             redo A;  
           } else {  
             !!!cp (27);  
             $self->{next_char} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1294          } else {          } else {
1295            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1296              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1297            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1298            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1299            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## 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_char} and        if (0x0041 <= $self->{nc} and
1308            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1309          !!!cp (29);          !!!cp (29);
1310          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct}
1311                            tag_name => chr ($self->{next_char} + 0x0020)};              = {type => END_TAG_TOKEN,
1312                   tag_name => chr ($self->{nc} + 0x0020),
1313                   line => $l, column => $c};
1314          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1315          !!!next-input-character;          !!!next-input-character;
1316          redo A;          redo A;
1317        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1318                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1319          !!!cp (30);          !!!cp (30);
1320          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1321                            tag_name => chr ($self->{next_char})};                                    tag_name => chr ($self->{nc}),
1322                                      line => $l, column => $c};
1323          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1327          !!!cp (31);          !!!cp (31);
1328          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag',
1329                            line => $self->{line_prev}, ## "<" in "</>"
1330                            column => $self->{column_prev} - 1);
1331          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1332          !!!next-input-character;          !!!next-input-character;
1333          redo A;          redo A;
1334        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1335          !!!cp (32);          !!!cp (32);
1336          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1337          $self->{state} = DATA_STATE;          $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);          !!!cp (33);
1347          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1348          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1349          ## $self->{next_char} 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} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1413        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1414          !!!cp (34);          !!!cp (34);
1415          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1416          !!!next-input-character;          !!!next-input-character;
1417          redo A;          redo A;
1418        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1419          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1420            !!!cp (35);            !!!cp (35);
1421            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1422                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1426            #  !!! cp (36);            #  !!! cp (36);
1427            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 670  sub _get_next_token ($) { Line 1429  sub _get_next_token ($) {
1429              !!!cp (37);              !!!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_STATE;          $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_char} and        } elsif (0x0041 <= $self->{nc} and
1441                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1442          !!!cp (38);          !!!cp (38);
1443          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $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_char} == -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            !!!cp (39);            !!!cp (39);
1452            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1453                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1457            #  !!! cp (40);            #  !!! cp (40);
1458            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 703  sub _get_next_token ($) { Line 1460  sub _get_next_token ($) {
1460              !!!cp (41);              !!!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_STATE;          $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_char} == 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_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (42);  
           #  
         } else {  
           !!!cp (43);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1475          redo A;          redo A;
1476        } else {        } else {
1477          !!!cp (44);          !!!cp (44);
1478          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $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} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1485        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1486          !!!cp (45);          !!!cp (45);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1491          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1492            !!!cp (46);            !!!cp (46);
1493            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1494                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (47);
1498              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1499            } else {            } else {
1500              !!!cp (48);              !!!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_STATE;          $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_char} and        } elsif (0x0041 <= $self->{nc} and
1512                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1513          !!!cp (49);          !!!cp (49);
1514          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1515                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1516                   value => '',
1517                   line => $self->{line}, column => $self->{column}};
1518          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1519          !!!next-input-character;          !!!next-input-character;
1520          redo A;          redo A;
1521        } elsif ($self->{next_char} == 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_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (50);  
           #  
         } else {  
           !!!cp (51);  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1525          redo A;          redo A;
1526        } elsif ($self->{next_char} == -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            !!!cp (52);            !!!cp (52);
1530            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1531                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (53);
1535              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1536            } else {            } else {
1537              !!!cp (54);              !!!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_STATE;          $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 {
# Line 819  sub _get_next_token ($) { Line 1550  sub _get_next_token ($) {
1550               0x0022 => 1, # "               0x0022 => 1, # "
1551               0x0027 => 1, # '               0x0027 => 1, # '
1552               0x003D => 1, # =               0x003D => 1, # =
1553              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1554            !!!cp (55);            !!!cp (55);
1555            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1556          } else {          } else {
1557            !!!cp (56);            !!!cp (56);
1558          }          }
1559          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{ca}
1560                                value => ''};              = {name => chr ($self->{nc}),
1561                   value => '',
1562                   line => $self->{line}, column => $self->{column}};
1563          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1564          !!!next-input-character;          !!!next-input-character;
1565          redo A;          redo A;
1566        }        }
1567      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } 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            !!!cp (57);            !!!cp (57);
1572            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1573            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1574          } else {          } else {
1575            !!!cp (58);            !!!cp (58);
1576            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1577              = $self->{current_attribute};              = $self->{ca};
1578          }          }
1579        }; # $before_leave        }; # $before_leave
1580    
1581        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1582          !!!cp (59);          !!!cp (59);
1583          $before_leave->();          $before_leave->();
1584          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1585          !!!next-input-character;          !!!next-input-character;
1586          redo A;          redo A;
1587        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1588          !!!cp (60);          !!!cp (60);
1589          $before_leave->();          $before_leave->();
1590          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1591          !!!next-input-character;          !!!next-input-character;
1592          redo A;          redo A;
1593        } elsif ($self->{next_char} == 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            !!!cp (61);            !!!cp (61);
1597            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1598                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1599            !!!cp (62);            !!!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_STATE;          $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_char} and        } elsif (0x0041 <= $self->{nc} and
1614                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1615          !!!cp (63);          !!!cp (63);
1616          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $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_char} == 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_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (64);  
           #  
         } else {  
           !!!cp (65);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1625          redo A;          redo A;
1626        } elsif ($self->{next_char} == -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            !!!cp (66);            !!!cp (66);
1631            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1632                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (67);
1636              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1637            } else {            } else {
# Line 924  sub _get_next_token ($) { Line 1639  sub _get_next_token ($) {
1639              !!!cp (68);              !!!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_STATE;          $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          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1652              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1653            !!!cp (69);            !!!cp (69);
1654            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1655          } else {          } else {
1656            !!!cp (70);            !!!cp (70);
1657          }          }
1658          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $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} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1664        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1665          !!!cp (71);          !!!cp (71);
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_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1670          !!!cp (72);          !!!cp (72);
1671          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1672          !!!next-input-character;          !!!next-input-character;
1673          redo A;          redo A;
1674        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1675          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1676            !!!cp (73);            !!!cp (73);
1677            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1678                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (74);
1682              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1683            } else {            } else {
# Line 976  sub _get_next_token ($) { Line 1685  sub _get_next_token ($) {
1685              !!!cp (75);              !!!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_STATE;          $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_char} and        } elsif (0x0041 <= $self->{nc} and
1697                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1698          !!!cp (76);          !!!cp (76);
1699          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1700                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1701                   value => '',
1702                   line => $self->{line}, column => $self->{column}};
1703          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1704          !!!next-input-character;          !!!next-input-character;
1705          redo A;          redo A;
1706        } elsif ($self->{next_char} == 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_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (77);  
           #  
         } else {  
           !!!cp (78);  
           !!!parse-error (type => 'nestc');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1710          redo A;          redo A;
1711        } elsif ($self->{next_char} == -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            !!!cp (79);            !!!cp (79);
1715            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1716                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (80);
1720              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1721            } else {            } else {
# Line 1025  sub _get_next_token ($) { Line 1723  sub _get_next_token ($) {
1723              !!!cp (81);              !!!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_STATE;          $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          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1736          $self->{current_attribute} = {name => chr ($self->{next_char}),              $self->{nc} == 0x0027) { # '
1737                                value => ''};            !!!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;          $self->{state} = ATTRIBUTE_NAME_STATE;
1747          !!!next-input-character;          !!!next-input-character;
1748          redo A;                  redo A;        
1749        }        }
1750      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1751        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP        
1752          !!!cp (83);          !!!cp (83);
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_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1757          !!!cp (84);          !!!cp (84);
1758          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1759          !!!next-input-character;          !!!next-input-character;
1760          redo A;          redo A;
1761        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1762          !!!cp (85);          !!!cp (85);
1763          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1764          ## reconsume          ## reconsume
1765          redo A;          redo A;
1766        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1767          !!!cp (86);          !!!cp (86);
1768          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1769          !!!next-input-character;          !!!next-input-character;
1770          redo A;          redo A;
1771        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1772          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1773            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1774            !!!cp (87);            !!!cp (87);
1775            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1776                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (88);
1780              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1781            } else {            } else {
# Line 1082  sub _get_next_token ($) { Line 1783  sub _get_next_token ($) {
1783              !!!cp (89);              !!!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_STATE;          $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_char} == -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            !!!cp (90);            !!!cp (90);
1798            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1799                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (91);
1803              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1804            } else {            } else {
# Line 1107  sub _get_next_token ($) { Line 1806  sub _get_next_token ($) {
1806              !!!cp (92);              !!!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_STATE;          $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          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1819            !!!cp (93);            !!!cp (93);
1820            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1821          } else {          } else {
1822            !!!cp (94);            !!!cp (94);
1823          }          }
1824          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1825          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1826          !!!next-input-character;          !!!next-input-character;
1827          redo A;          redo A;
1828        }        }
1829      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1830        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1831          !!!cp (95);          !!!cp (95);
1832          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1833          !!!next-input-character;          !!!next-input-character;
1834          redo A;          redo A;
1835        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1836          !!!cp (96);          !!!cp (96);
1837          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1838          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "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_char} == -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            !!!cp (97);            !!!cp (97);
1850            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1851                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (98);
1855              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1856            } else {            } else {
# Line 1156  sub _get_next_token ($) { Line 1858  sub _get_next_token ($) {
1858              !!!cp (99);              !!!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_STATE;          $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          !!!cp (100);          !!!cp (100);
1871          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $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} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1881        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1882          !!!cp (101);          !!!cp (101);
1883          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1884          !!!next-input-character;          !!!next-input-character;
1885          redo A;          redo A;
1886        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1887          !!!cp (102);          !!!cp (102);
1888          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1889          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "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_char} == -1) {        } elsif ($self->{nc} == -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            !!!cp (103);            !!!cp (103);
1901            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1902                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (104);
1906              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1907            } else {            } else {
# Line 1200  sub _get_next_token ($) { Line 1909  sub _get_next_token ($) {
1909              !!!cp (105);              !!!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_STATE;          $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          !!!cp (106);          !!!cp (106);
1922          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $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} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1932        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # HT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1933          !!!cp (107);          !!!cp (107);
1934          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1935          !!!next-input-character;          !!!next-input-character;
1936          redo A;          redo A;
1937        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1938          !!!cp (108);          !!!cp (108);
1939          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1940          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1941            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1942            ## implementation of the "consume a character reference" algorithm.
1943            $self->{entity_add} = -1;
1944            $self->{prev_state} = $self->{state};
1945            $self->{state} = ENTITY_STATE;
1946          !!!next-input-character;          !!!next-input-character;
1947          redo A;          redo A;
1948        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1949          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1950            !!!cp (109);            !!!cp (109);
1951            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1952                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (110);
1956              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1957            } else {            } else {
# Line 1247  sub _get_next_token ($) { Line 1959  sub _get_next_token ($) {
1959              !!!cp (111);              !!!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_STATE;          $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_char} == -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            !!!cp (112);            !!!cp (112);
1974            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1975                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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);              !!!cp (113);
1979              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1980            } else {            } else {
# Line 1272  sub _get_next_token ($) { Line 1982  sub _get_next_token ($) {
1982              !!!cp (114);              !!!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_STATE;          $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 {
# Line 1285  sub _get_next_token ($) { Line 1995  sub _get_next_token ($) {
1995               0x0022 => 1, # "               0x0022 => 1, # "
1996               0x0027 => 1, # '               0x0027 => 1, # '
1997               0x003D => 1, # =               0x003D => 1, # =
1998              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1999            !!!cp (115);            !!!cp (115);
2000            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
2001          } else {          } else {
2002            !!!cp (116);            !!!cp (116);
2003          }          }
2004          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $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        }        }
     } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {  
       my $token = $self->_tokenize_attempt_to_consume_an_entity  
           (1,  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '  
            -1);  
   
       unless (defined $token) {  
         !!!cp (117);  
         $self->{current_attribute}->{value} .= '&';  
       } else {  
         !!!cp (118);  
         $self->{current_attribute}->{value} .= $token->{data};  
         $self->{current_attribute}->{has_reference} = $token->{has_reference};  
         ## ISSUE: spec says "append the returned character token to the current attribute's value"  
       }  
   
       $self->{state} = $self->{last_attribute_value_state};  
       # next-input-character is already done  
       redo A;  
2013      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2014        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2015          !!!cp (118);          !!!cp (118);
2016          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2017          !!!next-input-character;          !!!next-input-character;
2018          redo A;          redo A;
2019        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2020          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2021            !!!cp (119);            !!!cp (119);
2022            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
2023                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
2024            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2025            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2026              !!!cp (120);              !!!cp (120);
2027              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2028            } else {            } else {
# Line 1344  sub _get_next_token ($) { Line 2030  sub _get_next_token ($) {
2030              !!!cp (121);              !!!cp (121);
2031            }            }
2032          } else {          } else {
2033            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2034          }          }
2035          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2036          !!!next-input-character;          !!!next-input-character;
2037    
2038          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2039    
2040          redo A;          redo A;
2041        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2042            !!!cp (122);
2043            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2044          !!!next-input-character;          !!!next-input-character;
2045          if ($self->{next_char} == 0x003E and # >          redo A;
2046              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2047              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2048            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2049            !!!cp (122);            !!!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 {          } else {
2060            !!!cp (123);            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!parse-error (type => 'nestc');  
2061          }          }
2062          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2063          # next-input-character is already done          ## Reconsume.
2064            !!!emit ($self->{ct}); # start tag or end tag
2065          redo A;          redo A;
2066        } else {        } else {
2067          !!!cp (124);          !!!cp ('124.1');
2068          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2069          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2070          ## reconsume          ## reconsume
2071          redo A;          redo A;
2072        }        }
2073      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2074        ## (only happen if PCDATA state)        if ($self->{nc} == 0x003E) { # >
2075                  if ($self->{ct}->{type} == END_TAG_TOKEN) {
2076        my $token = {type => COMMENT_TOKEN, data => ''};            !!!cp ('124.2');
2077              !!!parse-error (type => 'nestc', token => $self->{ct});
2078        BC: {            ## TODO: Different type than slash in start tag
2079          if ($self->{next_char} == 0x003E) { # >            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2080            !!!cp (124);            if ($self->{ct}->{attributes}) {
2081            $self->{state} = DATA_STATE;              !!!cp ('124.4');
2082            !!!next-input-character;              !!!parse-error (type => 'end tag attribute');
2083              } else {
2084            !!!emit ($token);              !!!cp ('124.5');
2085              }
2086              ## TODO: Test |<title></title/>|
2087            } else {
2088              !!!cp ('124.3');
2089              $self->{self_closing} = 1;
2090            }
2091    
2092            redo A;          $self->{state} = DATA_STATE;
2093          } elsif ($self->{next_char} == -1) {          !!!next-input-character;
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## 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            !!!cp (126);            die "$0: $self->{ct}->{type}: Unknown token type";
           $token->{data} .= chr ($self->{next_char});  
           !!!next-input-character;  
           redo BC;  
2113          }          }
2114        } # BC          $self->{state} = DATA_STATE;
2115            ## Reconsume.
2116        die "$0: _get_next_token: unexpected case [BC]";          !!!emit ($self->{ct}); # start tag or end tag
2117      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {          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_char};        ## consumes characters one-by-one basis.
2131                
2132        if ($self->{next_char} == 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_char};  
2137          if ($self->{next_char} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2138            !!!cp (127);          redo A;
2139            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};        } elsif ($self->{nc} == -1) {
2140            $self->{state} = COMMENT_START_STATE;          !!!cp (125);
2141            !!!next-input-character;          $self->{state} = DATA_STATE;
2142            redo A;          ## reconsume
2143          } else {  
2144            !!!cp (128);          !!!emit ($self->{ct}); # comment
2145          }          redo A;
2146        } elsif ($self->{next_char} == 0x0044 or # D        } else {
2147                 $self->{next_char} == 0x0064) { # d          !!!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_char};          redo A;
2156          if ($self->{next_char} == 0x004F or # O        }
2157              $self->{next_char} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2158            !!!next-input-character;        ## (only happen if PCDATA state)
2159            push @next_char, $self->{next_char};        
2160            if ($self->{next_char} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2161                $self->{next_char} == 0x0063) { # c          !!!cp (133);
2162              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2163              push @next_char, $self->{next_char};          !!!next-input-character;
2164              if ($self->{next_char} == 0x0054 or # T          redo A;
2165                  $self->{next_char} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2166                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2167                push @next_char, $self->{next_char};          ## ASCII case-insensitive.
2168                if ($self->{next_char} == 0x0059 or # Y          !!!cp (130);
2169                    $self->{next_char} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2170                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2171                  push @next_char, $self->{next_char};          !!!next-input-character;
2172                  if ($self->{next_char} == 0x0050 or # P          redo A;
2173                      $self->{next_char} == 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_char};                 $self->{nc} == 0x005B) { # [
2176                    if ($self->{next_char} == 0x0045 or # E          !!!cp (135.4);                
2177                        $self->{next_char} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2178                      !!!cp (129);          $self->{s_kwd} = '[';
2179                      ## TODO: What a stupid code this is!          !!!next-input-character;
2180                      $self->{state} = DOCTYPE_STATE;          redo A;
                     !!!next-input-character;  
                     redo A;  
                   } else {  
                     !!!cp (130);  
                   }  
                 } else {  
                   !!!cp (131);  
                 }  
               } else {  
                 !!!cp (132);  
               }  
             } else {  
               !!!cp (133);  
             }  
           } else {  
             !!!cp (134);  
           }  
         } else {  
           !!!cp (135);  
         }  
2181        } else {        } else {
2182          !!!cp (136);          !!!cp (136);
2183        }        }
2184    
2185        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2186        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2187        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2188          ## Reconsume.
2189        $self->{state} = BOGUS_COMMENT_STATE;        $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            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2199                                      line => $self->{line_prev},
2200                                      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) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2306        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2307          !!!cp (137);          !!!cp (137);
2308          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2309          !!!next-input-character;          !!!next-input-character;
2310          redo A;          redo A;
2311        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2312          !!!cp (138);          !!!cp (138);
2313          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2314          $self->{state} = DATA_STATE;          $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_char} == -1) {        } elsif ($self->{nc} == -1) {
2321          !!!cp (139);          !!!cp (139);
2322          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2323          $self->{state} = DATA_STATE;          $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          !!!cp (140);          !!!cp (140);
2331          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2332              .= chr ($self->{next_char});              .= chr ($self->{nc});
2333          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2334          !!!next-input-character;          !!!next-input-character;
2335          redo A;          redo A;
2336        }        }
2337      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2338        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2339          !!!cp (141);          !!!cp (141);
2340          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2341          !!!next-input-character;          !!!next-input-character;
2342          redo A;          redo A;
2343        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2344          !!!cp (142);          !!!cp (142);
2345          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2346          $self->{state} = DATA_STATE;          $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_char} == -1) {        } elsif ($self->{nc} == -1) {
2353          !!!cp (143);          !!!cp (143);
2354          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2355          $self->{state} = DATA_STATE;          $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          !!!cp (144);          !!!cp (144);
2363          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2364              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2365          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2366          !!!next-input-character;          !!!next-input-character;
2367          redo A;          redo A;
2368        }        }
2369      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2370        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2371          !!!cp (145);          !!!cp (145);
2372          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2373          !!!next-input-character;          !!!next-input-character;
2374          redo A;          redo A;
2375        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2376          !!!cp (146);          !!!cp (146);
2377          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2378          $self->{state} = DATA_STATE;          $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          !!!cp (147);          !!!cp (147);
2386          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $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} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2396        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2397          !!!cp (148);          !!!cp (148);
2398          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2399          !!!next-input-character;          !!!next-input-character;
2400          redo A;          redo A;
2401        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2402          !!!cp (149);          !!!cp (149);
2403          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2404          $self->{state} = DATA_STATE;          $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          !!!cp (150);          !!!cp (150);
2412          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2413          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2414          !!!next-input-character;          !!!next-input-character;
2415          redo A;          redo A;
2416        }        }
2417      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2418        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2419          !!!cp (151);          !!!cp (151);
2420          $self->{state} = DATA_STATE;          $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_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2427          !!!cp (152);          !!!cp (152);
2428          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2429          $self->{current_token}->{data} .= '-'; # comment                          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_char} == -1) {        } elsif ($self->{nc} == -1) {
2436          !!!cp (153);          !!!cp (153);
2437          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2438          $self->{state} = DATA_STATE;          $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          !!!cp (154);          !!!cp (154);
2446          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2447          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment                          line => $self->{line_prev},
2448                            column => $self->{column_prev});
2449            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2450          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2451          !!!next-input-character;          !!!next-input-character;
2452          redo A;          redo A;
2453        }        }
2454      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2455        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2456          !!!cp (155);          !!!cp (155);
2457          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2458          !!!next-input-character;          !!!next-input-character;
# Line 1643  sub _get_next_token ($) { Line 2465  sub _get_next_token ($) {
2465          redo A;          redo A;
2466        }        }
2467      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2468        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2469          !!!cp (157);          !!!cp (157);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2474          !!!cp (158);          !!!cp (158);
2475          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2476          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2477          !!!next-input-character;          !!!next-input-character;
2478    
2479          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2480    
2481          redo A;          redo A;
2482        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2483          !!!cp (159);          !!!cp (159);
2484          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2485          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2486          ## reconsume          ## reconsume
2487    
2488          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2489    
2490          redo A;          redo A;
2491        } else {        } else {
2492          !!!cp (160);          !!!cp (160);
2493          $self->{current_token}          $self->{ct}->{name} = chr $self->{nc};
2494              = {type => DOCTYPE_TOKEN,          delete $self->{ct}->{quirks};
                name => chr ($self->{next_char}),  
                #quirks => 0,  
               };  
 ## ISSUE: "Set the token's name name to the" in the spec  
2495          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        }        }
2499      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2500  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2501        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2502          !!!cp (161);          !!!cp (161);
2503          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2504          !!!next-input-character;          !!!next-input-character;
2505          redo A;          redo A;
2506        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2507          !!!cp (162);          !!!cp (162);
2508          $self->{state} = DATA_STATE;          $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_char} == -1) {        } elsif ($self->{nc} == -1) {
2515          !!!cp (163);          !!!cp (163);
2516          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2517          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2518          ## reconsume          ## reconsume
2519    
2520          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2521          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2522    
2523          redo A;          redo A;
2524        } else {        } else {
2525          !!!cp (164);          !!!cp (164);
2526          $self->{current_token}->{name}          $self->{ct}->{name}
2527            .= chr ($self->{next_char}); # DOCTYPE            .= 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} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2533        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2534          !!!cp (165);          !!!cp (165);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2539          !!!cp (166);          !!!cp (166);
2540          $self->{state} = DATA_STATE;          $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_char} == -1) {        } elsif ($self->{nc} == -1) {
2547          !!!cp (167);          !!!cp (167);
2548          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2549          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2550          ## reconsume          ## reconsume
2551    
2552          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2553          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2554    
2555          redo A;          redo A;
2556        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2557                 $self->{next_char} == 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_char} == 0x0055 or # U          redo A;
2562              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2563            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2564            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2565                $self->{next_char} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_char} == 0x004C or # L  
                 $self->{next_char} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0049 or # I  
                   $self->{next_char} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x0043 or # C  
                     $self->{next_char} == 0x0063) { # c  
                   !!!cp (168);  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (169);  
                 }  
               } else {  
                 !!!cp (170);  
               }  
             } else {  
               !!!cp (171);  
             }  
           } else {  
             !!!cp (172);  
           }  
         } else {  
           !!!cp (173);  
         }  
   
         #  
       } elsif ($self->{next_char} == 0x0053 or # S  
                $self->{next_char} == 0x0073) { # s  
2566          !!!next-input-character;          !!!next-input-character;
2567          if ($self->{next_char} == 0x0059 or # Y          redo A;
             $self->{next_char} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_char} == 0x0053 or # S  
               $self->{next_char} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0045 or # E  
                   $self->{next_char} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x004D or # M  
                     $self->{next_char} == 0x006D) { # m  
                   !!!cp (174);  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (175);  
                 }  
               } else {  
                 !!!cp (176);  
               }  
             } else {  
               !!!cp (177);  
             }  
           } else {  
             !!!cp (178);  
           }  
         } else {  
           !!!cp (179);  
         }  
   
         #  
2568        } else {        } else {
2569          !!!cp (180);          !!!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->{current_token}->{quirks} = 1;          ## Reconsume.
2614            redo A;
2615          }
2616        } elsif ($self->{state} == SYSTEM_STATE) {
2617          ## ASCII case-insensitive
2618          if ($self->{nc} == [
2619                undef,
2620                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;          $self->{state} = BOGUS_DOCTYPE_STATE;
2652        # next-input-character is already done          ## Reconsume.
2653        redo A;          redo A;
2654          }
2655      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2656        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2657          !!!cp (181);          !!!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_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2662          !!!cp (182);          !!!cp (182);
2663          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2664          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2665          !!!next-input-character;          !!!next-input-character;
2666          redo A;          redo A;
2667        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2668          !!!cp (183);          !!!cp (183);
2669          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2670          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2671          !!!next-input-character;          !!!next-input-character;
2672          redo A;          redo A;
2673        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2674          !!!cp (184);          !!!cp (184);
2675          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2676    
2677          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2678          !!!next-input-character;          !!!next-input-character;
2679    
2680          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2681          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2682    
2683          redo A;          redo A;
2684        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2685          !!!cp (185);          !!!cp (185);
2686          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2687    
2688          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2689          ## reconsume          ## reconsume
2690    
2691          $self->{current_token}->{quirks} = 1;          $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);          !!!cp (186);
2697          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2698          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2699    
2700          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2701          !!!next-input-character;          !!!next-input-character;
2702          redo A;          redo A;
2703        }        }
2704      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2705        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2706          !!!cp (187);          !!!cp (187);
2707          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2708          !!!next-input-character;          !!!next-input-character;
2709          redo A;          redo A;
2710        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2711          !!!cp (188);          !!!cp (188);
2712          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2713    
2714          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2715          !!!next-input-character;          !!!next-input-character;
2716    
2717          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2718          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2719    
2720          redo A;          redo A;
2721        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2722          !!!cp (189);          !!!cp (189);
2723          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2724    
2725          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2726          ## reconsume          ## reconsume
2727    
2728          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2729          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2730    
2731          redo A;          redo A;
2732        } else {        } else {
2733          !!!cp (190);          !!!cp (190);
2734          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2735              .= chr $self->{next_char};              .= 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} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2744        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2745          !!!cp (191);          !!!cp (191);
2746          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2747          !!!next-input-character;          !!!next-input-character;
2748          redo A;          redo A;
2749        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2750          !!!cp (192);          !!!cp (192);
2751          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2752    
2753          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2754          !!!next-input-character;          !!!next-input-character;
2755    
2756          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2757          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2758    
2759          redo A;          redo A;
2760        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2761          !!!cp (193);          !!!cp (193);
2762          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2763    
2764          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2765          ## reconsume          ## reconsume
2766    
2767          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2768          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2769    
2770          redo A;          redo A;
2771        } else {        } else {
2772          !!!cp (194);          !!!cp (194);
2773          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2774              .= chr $self->{next_char};              .= 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} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2783        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2784          !!!cp (195);          !!!cp (195);
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_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2789          !!!cp (196);          !!!cp (196);
2790          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2791          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2792          !!!next-input-character;          !!!next-input-character;
2793          redo A;          redo A;
2794        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2795          !!!cp (197);          !!!cp (197);
2796          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2797          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2798          !!!next-input-character;          !!!next-input-character;
2799          redo A;          redo A;
2800        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2801          !!!cp (198);          !!!cp (198);
2802          $self->{state} = DATA_STATE;          $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_char} == -1) {        } elsif ($self->{nc} == -1) {
2809          !!!cp (199);          !!!cp (199);
2810          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2811    
2812          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2813          ## reconsume          ## reconsume
2814    
2815          $self->{current_token}->{quirks} = 1;          $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);          !!!cp (200);
2821          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2822          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2823    
2824          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2825          !!!next-input-character;          !!!next-input-character;
2826          redo A;          redo A;
2827        }        }
2828      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2829        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2830          !!!cp (201);          !!!cp (201);
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_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2835          !!!cp (202);          !!!cp (202);
2836          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2837          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2838          !!!next-input-character;          !!!next-input-character;
2839          redo A;          redo A;
2840        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2841          !!!cp (203);          !!!cp (203);
2842          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2843          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845          redo A;          redo A;
2846        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2847          !!!cp (204);          !!!cp (204);
2848          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2849          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2850          !!!next-input-character;          !!!next-input-character;
2851    
2852          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2853          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2854    
2855          redo A;          redo A;
2856        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2857          !!!cp (205);          !!!cp (205);
2858          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2859    
2860          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2861          ## reconsume          ## reconsume
2862    
2863          $self->{current_token}->{quirks} = 1;          $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);          !!!cp (206);
2869          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2870          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2871    
2872          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2873          !!!next-input-character;          !!!next-input-character;
2874          redo A;          redo A;
2875        }        }
2876      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2877        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2878          !!!cp (207);          !!!cp (207);
2879          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2880          !!!next-input-character;          !!!next-input-character;
2881          redo A;          redo A;
2882        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2883          !!!cp (208);          !!!cp (208);
2884          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2885    
2886          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2887          !!!next-input-character;          !!!next-input-character;
2888    
2889          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2890          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2891    
2892          redo A;          redo A;
2893        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2894          !!!cp (209);          !!!cp (209);
2895          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2896    
2897          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2898          ## reconsume          ## reconsume
2899    
2900          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2901          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2902    
2903          redo A;          redo A;
2904        } else {        } else {
2905          !!!cp (210);          !!!cp (210);
2906          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2907              .= chr $self->{next_char};              .= 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} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2916        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2917          !!!cp (211);          !!!cp (211);
2918          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2919          !!!next-input-character;          !!!next-input-character;
2920          redo A;          redo A;
2921        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2922          !!!cp (212);          !!!cp (212);
2923          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2924    
2925          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2926          !!!next-input-character;          !!!next-input-character;
2927    
2928          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2929          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2930    
2931          redo A;          redo A;
2932        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2933          !!!cp (213);          !!!cp (213);
2934          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2935    
2936          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2937          ## reconsume          ## reconsume
2938    
2939          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2940          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2941    
2942          redo A;          redo A;
2943        } else {        } else {
2944          !!!cp (214);          !!!cp (214);
2945          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2946              .= chr $self->{next_char};              .= 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} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2955        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2956          !!!cp (215);          !!!cp (215);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2961          !!!cp (216);          !!!cp (216);
2962          $self->{state} = DATA_STATE;          $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_char} == -1) {        } elsif ($self->{nc} == -1) {
2969          !!!cp (217);          !!!cp (217);
2970          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2971          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2972          ## reconsume          ## reconsume
2973    
2974          $self->{current_token}->{quirks} = 1;          $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);          !!!cp (218);
2980          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2981          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2982    
2983          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2984          !!!next-input-character;          !!!next-input-character;
2985          redo A;          redo A;
2986        }        }
2987      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2988        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2989          !!!cp (219);          !!!cp (219);
2990          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2991          !!!next-input-character;          !!!next-input-character;
2992    
2993          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2994    
2995          redo A;          redo A;
2996        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2997          !!!cp (220);          !!!cp (220);
         !!!parse-error (type => 'unclosed DOCTYPE');  
2998          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2999          ## reconsume          ## reconsume
3000    
3001          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
3002    
3003          redo A;          redo A;
3004        } else {        } else {
3005          !!!cp (221);          !!!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, $additional) = @_;          !!!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         $additional => 1,          !!!cp (221.5);
3050        }->{$self->{next_char}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3051      !!!cp (1001);          !!!next-input-character;
3052      ## Don't consume          redo A;
3053      ## No error        } else {
3054      return undef;          !!!cp (221.6);
3055    } elsif ($self->{next_char} == 0x0023) { # #          $self->{ct}->{data} .= ']';
3056      !!!next-input-character;          $self->{state} = CDATA_SECTION_STATE;
3057      if ($self->{next_char} == 0x0078 or # x          ## Reconsume.
3058          $self->{next_char} == 0x0058) { # X          redo A;
3059        my $code;        }
3060        X: {      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3061          my $x_char = $self->{next_char};        if ($self->{nc} == 0x003E) { # >
3062          !!!next-input-character;          $self->{state} = DATA_STATE;
3063          if (0x0030 <= $self->{next_char} and          !!!next-input-character;
3064              $self->{next_char} <= 0x0039) { # 0..9          if (length $self->{ct}->{data}) { # character
3065            !!!cp (1002);            !!!cp (221.7);
3066            $code ||= 0;            !!!emit ($self->{ct}); # character
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0030;  
           redo X;  
         } elsif (0x0061 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0066) { # a..f  
           !!!cp (1003);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0046) { # A..F  
           !!!cp (1004);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!cp (1005);  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_char});  
           $self->{next_char} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_char} == 0x003B) { # ;  
           !!!cp (1006);  
           !!!next-input-character;  
3067          } else {          } else {
3068            !!!cp (1007);            !!!cp (221.8);
3069            !!!parse-error (type => 'no refc');            ## No token to emit. $self->{ct} is discarded.
3070          }          }
3071            redo A;
3072          } elsif ($self->{nc} == 0x005D) { # ]
3073            !!!cp (221.9); # character
3074            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3075            ## Stay in the state.
3076            !!!next-input-character;
3077            redo A;
3078          } else {
3079            !!!cp (221.11);
3080            $self->{ct}->{data} .= ']]'; # character
3081            $self->{state} = CDATA_SECTION_STATE;
3082            ## Reconsume.
3083            redo A;
3084          }
3085        } elsif ($self->{state} == ENTITY_STATE) {
3086          if ($is_space->{$self->{nc}} or
3087              {
3088                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3089                $self->{entity_add} => 1,
3090              }->{$self->{nc}}) {
3091            !!!cp (1001);
3092            ## Don't consume
3093            ## No error
3094            ## Return nothing.
3095            #
3096          } 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;
3113            redo A;
3114          } else {
3115            !!!cp (1027);
3116            !!!parse-error (type => 'bare ero');
3117            ## Return nothing.
3118            #
3119          }
3120    
3121          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        ## NOTE: No character is consumed by the "consume a character
3122            !!!cp (1008);        ## reference" algorithm.  In other word, there is an "&" character
3123            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        ## that does not introduce a character reference, which would be
3124            $code = 0xFFFD;        ## appended to the parent element or the attribute value in later
3125          } elsif ($code > 0x10FFFF) {        ## process of the tokenizer.
3126            !!!cp (1009);  
3127            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);        if ($self->{prev_state} == DATA_STATE) {
3128            $code = 0xFFFD;          !!!cp (997);
3129          } elsif ($code == 0x000D) {          $self->{state} = $self->{prev_state};
3130            !!!cp (1010);          ## Reconsume.
3131            !!!parse-error (type => 'CR character reference');          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3132            $code = 0x000A;                    line => $self->{line_prev},
3133          } elsif (0x80 <= $code and $code <= 0x9F) {                    column => $self->{column_prev},
3134            !!!cp (1011);                   });
3135            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          redo A;
3136            $code = $c1_entity_char->{$code};        } else {
3137          }          !!!cp (996);
3138            $self->{ca}->{value} .= '&';
3139          return {type => CHARACTER_TOKEN, data => chr $code,          $self->{state} = $self->{prev_state};
3140                  has_reference => 1};          ## Reconsume.
3141        } # X          redo A;
3142      } elsif (0x0030 <= $self->{next_char} and        }
3143               $self->{next_char} <= 0x0039) { # 0..9      } elsif ($self->{state} == ENTITY_HASH_STATE) {
3144        my $code = $self->{next_char} - 0x0030;        if ($self->{nc} == 0x0078 or # x
3145        !!!next-input-character;            $self->{nc} == 0x0058) { # X
3146                  !!!cp (995);
3147        while (0x0030 <= $self->{next_char} and          $self->{state} = HEXREF_X_STATE;
3148                  $self->{next_char} <= 0x0039) { # 0..9          $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;
3157            redo A;
3158          } 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);          !!!cp (1012);
3189          $code *= 10;          $self->{s_kwd} *= 10;
3190          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3191                    
3192            ## Stay in the state.
3193          !!!next-input-character;          !!!next-input-character;
3194        }          redo A;
3195          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3196          !!!cp (1013);          !!!cp (1013);
3197          !!!next-input-character;          !!!next-input-character;
3198            #
3199        } else {        } else {
3200          !!!cp (1014);          !!!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          my $l = $self->{line_prev};
3208          my $c = $self->{column_prev};
3209          if ($charref_map->{$code}) {
3210          !!!cp (1015);          !!!cp (1015);
3211          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!parse-error (type => 'invalid character reference',
3212          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3213                            line => $l, column => $c);
3214            $code = $charref_map->{$code};
3215        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3216          !!!cp (1016);          !!!cp (1016);
3217          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!parse-error (type => 'invalid character reference',
3218                            text => (sprintf 'U-%08X', $code),
3219                            line => $l, column => $c);
3220          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!cp (1017);  
         !!!parse-error (type => 'CR character reference');  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!cp (1018);  
         !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
         $code = $c1_entity_char->{$code};  
3221        }        }
3222          
3223        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3224      } else {          !!!cp (992);
3225        !!!cp (1019);          $self->{state} = $self->{prev_state};
3226        !!!parse-error (type => 'bare nero');          ## Reconsume.
3227        !!!back-next-input-character ($self->{next_char});          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3228        $self->{next_char} = 0x0023; # #                    line => $l, column => $c,
3229        return undef;                   });
3230      }          redo A;
3231    } elsif ((0x0041 <= $self->{next_char} and        } else {
3232              $self->{next_char} <= 0x005A) or          !!!cp (991);
3233             (0x0061 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3234              $self->{next_char} <= 0x007A)) {          $self->{ca}->{has_reference} = 1;
3235      my $entity_name = chr $self->{next_char};          $self->{state} = $self->{prev_state};
3236      !!!next-input-character;          ## Reconsume.
3237            redo A;
3238      my $value = $entity_name;        }
3239      my $match = 0;      } elsif ($self->{state} == HEXREF_X_STATE) {
3240      require Whatpm::_NamedEntityList;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3241      our $EntityChar;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3242              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3243      while (length $entity_name < 10 and          # 0..9, A..F, a..f
3244             ## NOTE: Some number greater than the maximum length of entity name          !!!cp (990);
3245             ((0x0041 <= $self->{next_char} and # a          $self->{state} = HEXREF_HEX_STATE;
3246               $self->{next_char} <= 0x005A) or # x          $self->{s_kwd} = 0;
3247              (0x0061 <= $self->{next_char} and # a          ## Reconsume.
3248               $self->{next_char} <= 0x007A) or # z          redo A;
3249              (0x0030 <= $self->{next_char} and # 0        } else {
3250               $self->{next_char} <= 0x0039) or # 9          !!!parse-error (type => 'bare hcro',
3251              $self->{next_char} == 0x003B)) { # ;                          line => $self->{line_prev},
3252        $entity_name .= chr $self->{next_char};                          column => $self->{column_prev} - 2);
3253        if (defined $EntityChar->{$entity_name}) {  
3254          if ($self->{next_char} == 0x003B) { # ;          ## NOTE: According to the spec algorithm, nothing is returned,
3255            !!!cp (1020);          ## and then "&#" followed by "X" or "x" is appended to the parent
3256            $value = $EntityChar->{$entity_name};          ## element or the attribute value in the later processing.
3257            $match = 1;  
3258            !!!next-input-character;          if ($self->{prev_state} == DATA_STATE) {
3259            last;            !!!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 {          } else {
3269            !!!cp (1021);            !!!cp (989);
3270            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3271            $match = -1;            $self->{state} = $self->{prev_state};
3272            !!!next-input-character;            ## Reconsume.
3273              redo A;
3274          }          }
3275        } else {        }
3276          !!!cp (1022);      } elsif ($self->{state} == HEXREF_HEX_STATE) {
3277          $value .= chr $self->{next_char};        if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3278          $match *= 2;          # 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;          !!!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      if ($match > 0) {        my $l = $self->{line_prev};
3316        !!!cp (1023);        my $c = $self->{column_prev};
3317        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($charref_map->{$code}) {
3318      } elsif ($match < 0) {          !!!cp (1008);
3319        !!!parse-error (type => 'no refc');          !!!parse-error (type => 'invalid character reference',
3320        if ($in_attr and $match < -1) {                          text => (sprintf 'U+%04X', $code),
3321          !!!cp (1024);                          line => $l, column => $c);
3322          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          $code = $charref_map->{$code};
3323          } elsif ($code > 0x10FFFF) {
3324            !!!cp (1009);
3325            !!!parse-error (type => 'invalid character reference',
3326                            text => (sprintf 'U-%08X', $code),
3327                            line => $l, column => $c);
3328            $code = 0xFFFD;
3329          }
3330    
3331          if ($self->{prev_state} == DATA_STATE) {
3332            !!!cp (988);
3333            $self->{state} = $self->{prev_state};
3334            ## Reconsume.
3335            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3336                      line => $l, column => $c,
3337                     });
3338            redo A;
3339          } else {
3340            !!!cp (987);
3341            $self->{ca}->{value} .= chr $code;
3342            $self->{ca}->{has_reference} = 1;
3343            $self->{state} = $self->{prev_state};
3344            ## Reconsume.
3345            redo A;
3346          }
3347        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3348          if (length $self->{s_kwd} < 30 and
3349              ## NOTE: Some number greater than the maximum length of entity name
3350              ((0x0041 <= $self->{nc} and # a
3351                $self->{nc} <= 0x005A) or # x
3352               (0x0061 <= $self->{nc} and # a
3353                $self->{nc} <= 0x007A) or # z
3354               (0x0030 <= $self->{nc} and # 0
3355                $self->{nc} <= 0x0039) or # 9
3356               $self->{nc} == 0x003B)) { # ;
3357            our $EntityChar;
3358            $self->{s_kwd} .= chr $self->{nc};
3359            if (defined $EntityChar->{$self->{s_kwd}}) {
3360              if ($self->{nc} == 0x003B) { # ;
3361                !!!cp (1020);
3362                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3363                $self->{entity__match} = 1;
3364                !!!next-input-character;
3365                #
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 {
3375              !!!cp (1022);
3376              $self->{entity__value} .= chr $self->{nc};
3377              $self->{entity__match} *= 2;
3378              ## Stay in the state.
3379              !!!next-input-character;
3380              redo A;
3381            }
3382          }
3383    
3384          my $data;
3385          my $has_ref;
3386          if ($self->{entity__match} > 0) {
3387            !!!cp (1023);
3388            $data = $self->{entity__value};
3389            $has_ref = 1;
3390            #
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          !!!cp (1025);          !!!cp (1026);
3406          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!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        !!!cp (1026);        die "$0: $self->{state}: Unknown state";
       !!!parse-error (type => 'bare ero');  
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value};  
3443      }      }
3444    } else {    } # A  
3445      !!!cp (1027);  
3446      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3447      !!!parse-error (type => 'bare ero');  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3448    
3449  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3450    my $self = shift;    my $self = shift;
# Line 2400  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 2419  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    
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.    ## NOTE: The "initial" insertion mode.
3488    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 2454  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}) {
           defined $token->{system_identifier}) {  
3513          !!!cp ('t1');          !!!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          !!!cp ('t2');          !!!cp ('t2');
3517          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!parse-error (type => 'not HTML5', token => $token);
3518          !!!parse-error (type => 'not HTML5');        } 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 {        } else {
3527          !!!cp ('t3');          !!!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);
# Line 2481  sub _tree_construction_initial ($) { Line 3541  sub _tree_construction_initial ($) {
3541        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3542          !!!cp ('t4');          !!!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            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3597            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3598            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3599            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3600            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3601            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3602            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3603            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3604            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3605            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3606            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3607            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3608            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3609            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3610            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3611            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3612            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3613            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3614            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
           "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,  
           "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,  
           "HTML" => 1,  
         }->{$pubid}) {  
3615            !!!cp ('t5');            !!!cp ('t5');
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');              !!!cp ('t6');
3621              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3622            } else {            } else {
3623              !!!cp ('t7');              !!!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');            !!!cp ('t8');
3629            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3630          } else {          } else {
# Line 2579  sub _tree_construction_initial ($) { Line 3633  sub _tree_construction_initial ($) {
3633        } else {        } else {
3634          !!!cp ('t10');          !!!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            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## 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');            !!!cp ('t11');
3644          } else {          } else {
# Line 2602  sub _tree_construction_initial ($) { Line 3657  sub _tree_construction_initial ($) {
3657                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3658               }->{$token->{type}}) {               }->{$token->{type}}) {
3659        !!!cp ('t14');        !!!cp ('t14');
3660        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3661        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3662        ## Go to the "before html" insertion mode.        ## 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}) {
# Line 2623  sub _tree_construction_initial ($) { Line 3679  sub _tree_construction_initial ($) {
3679          !!!cp ('t17');          !!!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 "before html" insertion mode.        ## Go to the "before html" insertion mode.
3685        ## reprocess        ## reprocess
# Line 2652  sub _tree_construction_root_element ($) Line 3708  sub _tree_construction_root_element ($)
3708    B: {    B: {
3709        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3710          !!!cp ('t19');          !!!cp ('t19');
3711          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3712          ## Ignore the token          ## Ignore the token
3713          ## Stay in the insertion mode.          ## Stay in the insertion mode.
3714          !!!next-token;          !!!next-token;
# Line 2665  sub _tree_construction_root_element ($) Line 3721  sub _tree_construction_root_element ($)
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}) {
# Line 2686  sub _tree_construction_root_element ($) Line 3742  sub _tree_construction_root_element ($)
3742        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3743          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3744            my $root_element;            my $root_element;
3745            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes});            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3746            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3747            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3748                  [$root_element, $el_category->{html}];
3749    
3750            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3751              !!!cp ('t24');              !!!cp ('t24');
3752              $self->{application_cache_selection}              $self->{application_cache_selection}
3753                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3754              ## ISSUE: No relative reference resolution?              ## 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 {            } else {
3759              !!!cp ('t25');              !!!cp ('t25');
3760              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3761            }            }
3762    
3763              !!!nack ('t25c');
3764    
3765            !!!next-token;            !!!next-token;
3766            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3767          } else {          } else {
# Line 2716  sub _tree_construction_root_element ($) Line 3778  sub _tree_construction_root_element ($)
3778          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3779        }        }
3780    
3781      my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3782        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3783      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3784      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3785    
3786      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3787    
3788      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3789        !!!ack-later;
3790      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3791    } # B    } # B
3792    
3793    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 2743  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              !!!cp ('t27');          } else {
3814              #            die "_reset_insertion_mode: t27";
           } else {  
             !!!cp ('t28');  
             $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                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3839                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
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 2779  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');            !!!cp ('t29');
3858            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2797  sub _reset_insertion_mode ($) { Line 3866  sub _reset_insertion_mode ($) {
3866          !!!cp ('t31');          !!!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    
# Line 2908  sub _tree_construction_main ($) { Line 3977  sub _tree_construction_main ($) {
3977      !!!cp ('t39');      !!!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  
       !!!cp ('t40');  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
3995    
3996      ## Step 7      !!!nack ('t40.1');
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!cp ('t43');  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!cp ('t44');  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
     } else {  
       die "$0: $content_model_flag in parse_rcdata";  
     }  
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) {  
       !!!cp ('t45');  
       $text .= $token->{data};  
       !!!next-token;  
     } # stop if non-character token or tokenizer stops tokenising  
     if (length $text) {  
       !!!cp ('t46');  
       $script_el->manakai_append_text ($text);  
     }  
                 
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
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;
       !!!cp ('t47');  
       ## Ignore the token  
     } else {  
       !!!cp ('t48');  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
       ## ISSUE: And ignore?  
       ## TODO: mark as "already executed"  
     }  
       
     if (defined $self->{inner_html_node}) {  
       !!!cp ('t49');  
       ## TODO: mark as "already executed"  
     } else {  
       !!!cp ('t50');  
       ## TODO: $old_insertion_point = current insertion point  
       ## TODO: insertion point = just before the next input character  
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');            !!!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') {  
           !!!cp ('t52');  
           last AFE;  
4053          }          }
4054        } # AFE        } # AFE
4055        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4056          !!!cp ('t53');          !!!cp ('t53');
4057          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!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 3048  sub _tree_construction_main ($) { Line 4071  sub _tree_construction_main ($) {
4071              last INSCOPE;              last INSCOPE;
4072            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4073              !!!cp ('t55');              !!!cp ('t55');
4074              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!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) {
                   table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4082            !!!cp ('t56');            !!!cp ('t56');
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          !!!cp ('t57');          !!!cp ('t57');
4088          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!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          !!!cp ('t58');          !!!cp ('t58');
4097          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!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 3078  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');            !!!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');            !!!cp ('t60');
4118            last OE;            last OE;
# Line 3167  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 3206  sub _tree_construction_main ($) { Line 4259  sub _tree_construction_main ($) {
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');                                 !!!cp ('t70');
# Line 3245  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                           !!!cp ('t72');      } else {
4300                           $self->{open_elements}->[-1]->[0]->append_child ($child);        !!!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        !!!cp ('t73');        !!!cp ('t73');
4327        !!!parse-error (type => 'DOCTYPE in the middle');        !!!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) {  
         !!!cp ('t74');  
         #  
       } else {  
         ## Generate implied end tags  
         while ({  
                 dd => 1, dt => 1, li => 1, p => 1,  
                }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!cp ('t75');  
           pop @{$self->{open_elements}};  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!cp ('t76');  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
 ## ISSUE: This case is never reached.  
           !!!cp ('t77');  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } else {  
           !!!cp ('t78');  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
   
       ## Stop parsing  
       last B;  
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          !!!cp ('t79');          !!!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          !!!cp ('t80');          !!!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 {        } else {
4343          !!!cp ('t81');          !!!cp ('t81');
4344        }        }
4345    
4346        !!!cp ('t82');        !!!cp ('t82');
4347        !!!parse-error (type => 'not first start tag');        !!!parse-error (type => 'not first start tag', token => $token);
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)) {
# Line 3318  sub _tree_construction_main ($) { Line 4354  sub _tree_construction_main ($) {
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) {
# Line 3331  sub _tree_construction_main ($) { Line 4368  sub _tree_construction_main ($) {
4368        } else {        } else {
4369          !!!cp ('t87');          !!!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');              !!!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');            !!!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}};
# Line 3360  sub _tree_construction_main ($) { Line 4613  sub _tree_construction_main ($) {
4613            !!!cp ('t90');            !!!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 3376  sub _tree_construction_main ($) { Line 4629  sub _tree_construction_main ($) {
4629            !!!cp ('t92');            !!!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                  !!!cp ('t93');              !!!cp ('t93');
4642                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4643                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4644                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4645                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4646                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4647                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4648                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4649                  !!!cp ('t94');              !!!next-token;
4650                  #              next B;
4651                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4652                  !!!cp ('t95');              !!!cp ('t93.2');
4653                  !!!parse-error (type => 'in head:head'); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4654                  ## Ignore the token                              token => $token);
4655                  !!!next-token;              ## Ignore the token
4656                  redo B;              !!!nack ('t93.3');
4657                }              !!!next-token;
4658              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4659                !!!cp ('t96');            } else {
4660                ## As if <head>              !!!cp ('t95');
4661                !!!create-element ($self->{head_element}, 'head');              !!!parse-error (type => 'in head:head',
4662                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4663                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## 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 {          } else {
4679                !!!cp ('t97');            !!!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                  !!!cp ('t98');              !!!cp ('t98');
4685                  ## As if </noscript>              ## As if </noscript>
4686                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4687                  !!!parse-error (type => 'in noscript:base');              !!!parse-error (type => 'in noscript', text => 'base',
4688                                              token => $token);
4689                  $self->{insertion_mode} = IN_HEAD_IM;            
4690                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4691                } else {              ## Reprocess in the "in head" insertion mode...
4692                  !!!cp ('t99');            } 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                  !!!cp ('t100');              !!!cp ('t100');
4699                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4700                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4701                } else {              push @{$self->{open_elements}},
4702                  !!!cp ('t101');                  [$self->{head_element}, $el_category->{head}];
4703                }              $self->{head_element_inserted} = 1;
4704                !!!insert-element ($token->{tag_name}, $token->{attributes});            } else {
4705                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              !!!cp ('t101');
4706                pop @{$self->{open_elements}}            }
4707                    if $self->{insertion_mode} == AFTER_HEAD_IM;            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4708                !!!next-token;            pop @{$self->{open_elements}};
4709                redo B;            pop @{$self->{open_elements}} # <head>
4710              } elsif ($token->{tag_name} eq 'link') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4711                ## NOTE: There is a "as if in head" code clone.            !!!nack ('t101.1');
4712                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            !!!next-token;
4713                  !!!cp ('t102');            next B;
4714                  !!!parse-error (type => 'after head:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'link') {
4715                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            ## NOTE: There is a "as if in head" code clone.
4716                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4717                  !!!cp ('t103');              !!!cp ('t102');
4718                }              !!!parse-error (type => 'after head',
4719                !!!insert-element ($token->{tag_name}, $token->{attributes});                              text => $token->{tag_name}, token => $token);
4720                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              push @{$self->{open_elements}},
4721                pop @{$self->{open_elements}}                  [$self->{head_element}, $el_category->{head}];
4722                    if $self->{insertion_mode} == AFTER_HEAD_IM;              $self->{head_element_inserted} = 1;
4723                !!!next-token;            } else {
4724                redo B;              !!!cp ('t103');
4725              } elsif ($token->{tag_name} eq 'meta') {            }
4726                ## NOTE: There is a "as if in head" code clone.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4727                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}};
4728                  !!!cp ('t104');            pop @{$self->{open_elements}} # <head>
4729                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4730                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!ack ('t103.1');
4731                } else {            !!!next-token;
4732                  !!!cp ('t105');            next B;
4733                }          } elsif ($token->{tag_name} eq 'command' or
4734                !!!insert-element ($token->{tag_name}, $token->{attributes});                   $token->{tag_name} eq 'eventsource') {
4735                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            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                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4772                    !!!cp ('t106');                    !!!cp ('t106');
4773                      ## NOTE: Whether the encoding is supported or not is handled
4774                      ## in the {change_encoding} callback.
4775                    $self->{change_encoding}                    $self->{change_encoding}
4776                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4777                             $token);
4778                                        
4779                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4780                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4781                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4782                                                 ->{has_reference});                                                 ->{has_reference});
4783                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4784                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4785                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4786                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4787                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4788                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4789                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4790                      !!!cp ('t107');                      !!!cp ('t107');
4791                        ## NOTE: Whether the encoding is supported or not is handled
4792                        ## in the {change_encoding} callback.
4793                      $self->{change_encoding}                      $self->{change_encoding}
4794                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4795                               $token);
4796                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4797                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4798                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
# Line 3514  sub _tree_construction_main ($) { Line 4818  sub _tree_construction_main ($) {
4818                  }                  }
4819                }                }
4820    
4821                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
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                  !!!cp ('t111');              !!!cp ('t111');
4829                  ## As if </noscript>              ## As if </noscript>
4830                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4831                  !!!parse-error (type => 'in noscript:title');              !!!parse-error (type => 'in noscript', text => 'title',
4832                                              token => $token);
4833                  $self->{insertion_mode} = IN_HEAD_IM;            
4834                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4835                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4836                  !!!cp ('t112');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4837                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t112');
4838                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4839                } else {                              text => $token->{tag_name}, token => $token);
4840                  !!!cp ('t113');              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                  !!!cp ('t114');              !!!parse-error (type => 'after head',
4861                  !!!parse-error (type => 'after head:'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
4862                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              push @{$self->{open_elements}},
4863                } else {                  [$self->{head_element}, $el_category->{head}];
4864                  !!!cp ('t115');              $self->{head_element_inserted} = 1;
4865                }            } else {
4866                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);              !!!cp ('t115');
4867                pop @{$self->{open_elements}}            }
4868                    if $self->{insertion_mode} == AFTER_HEAD_IM;            $parse_rcdata->(CDATA_CONTENT_MODEL);
4869                redo B;            ## ISSUE: A spec bug [Bug 6038]
4870              } elsif ($token->{tag_name} eq 'noscript') {            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');                  !!!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                  !!!cp ('t117');                  !!!cp ('t117');
4884                  !!!parse-error (type => 'in noscript:noscript');                  !!!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');                  !!!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                  !!!cp ('t119');              !!!cp ('t119');
4897                  ## As if </noscript>              ## As if </noscript>
4898                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4899                  !!!parse-error (type => 'in noscript:script');              !!!parse-error (type => 'in noscript', text => 'script',
4900                                              token => $token);
4901                  $self->{insertion_mode} = IN_HEAD_IM;            
4902                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4903                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4904                  !!!cp ('t120');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4905                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t120');
4906                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4907                } else {                              text => $token->{tag_name}, token => $token);
4908                  !!!cp ('t121');              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');                  !!!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 3621  sub _tree_construction_main ($) { Line 4942  sub _tree_construction_main ($) {
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');                  !!!cp ('t126');
4948                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
# Line 3631  sub _tree_construction_main ($) { Line 4952  sub _tree_construction_main ($) {
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');                !!!cp ('t128');
4960                #                #
# Line 3642  sub _tree_construction_main ($) { Line 4964  sub _tree_construction_main ($) {
4964                !!!cp ('t129');                !!!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 3661  sub _tree_construction_main ($) { Line 4984  sub _tree_construction_main ($) {
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');                  !!!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');                  !!!cp ('t133');
5009                  ## As if </noscript>                  ## As if </noscript>
5010                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5011                  !!!parse-error (type => 'in noscript:/head');                  !!!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');                  !!!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                  !!!cp ('t135');                  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) {
# Line 3706  sub _tree_construction_main ($) { Line 5038  sub _tree_construction_main ($) {
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                           $self->{insertion_mode} == AFTER_HEAD_IM) {
5044                  !!!cp ('t137');                  !!!cp ('t137');
5045                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!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');                  !!!cp ('t138');
5052                  #                  #
# Line 3720  sub _tree_construction_main ($) { Line 5054  sub _tree_construction_main ($) {
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                  !!!cp ('t139');                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5059                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_IM or
5060                  !!!create-element ($self->{head_element}, 'head');                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
   
                 $self->{insertion_mode} = IN_HEAD_IM;  
                 ## Reprocess in the "in head" insertion mode...  
               } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
5061                  !!!cp ('t140');                  !!!cp ('t140');
5062                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5063                                    text => $token->{tag_name}, token => $token);
5064                    ## Ignore the token
5065                    !!!next-token;
5066                    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 {                } else {
5075                  !!!cp ('t141');                  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                  !!!cp ('t142');                  !!!cp ('t142.2');
5087                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5088                  !!!create-element ($self->{head_element}, 'head');                  !!!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                } else {                  ## As if </head>
5111                  !!!cp ('t143');                  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');
                 !!!cp ('t144');  
5117                  #                  #
5118                } else {                } else {
5119                  !!!cp ('t145');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
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');                !!!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 3790  sub _tree_construction_main ($) { Line 5155  sub _tree_construction_main ($) {
5155              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5156  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5157                !!!cp ('t148');                !!!cp ('t148');
5158                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!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 {              } else {
5164                !!!cp ('t149');                !!!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            ## ISSUE: An issue in the spec.            ## 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              #
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');              !!!cp ('t150');
# Line 3818  sub _tree_construction_main ($) { Line 5236  sub _tree_construction_main ($) {
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 3826  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                      !!!cp ('t151');                      !!!cp ('t151');
5251                      $tn = $node->[1];  
5252                      last INSCOPE;                      ## Close the cell
5253                    } elsif ({                      !!!back-token; # <x>
5254                              table => 1, html => 1,                      $token = {type => END_TAG_TOKEN,
5255                             }->{$node->[1]}) {                                tag_name => $node->[0]->manakai_local_name,
5256                                  line => $token->{line},
5257                                  column => $token->{column}};
5258                        next B;
5259                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5260                      !!!cp ('t152');                      !!!cp ('t152');
5261                      last INSCOPE;                      ## ISSUE: This case can never be reached, maybe.
5262                        last;
5263                    }                    }
5264                  } # INSCOPE                  }
5265                    unless (defined $tn) {  
5266                      !!!cp ('t153');                  !!!cp ('t153');
5267  ## TODO: This error type is wrong.                  !!!parse-error (type => 'start tag not allowed',
5268                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      text => $token->{tag_name}, token => $token);
5269                      ## Ignore the token                  ## Ignore the token
5270                      !!!next-token;                  !!!nack ('t153.1');
5271                      redo B;                  !!!next-token;
5272                    }                  next B;
                   
                 !!!cp ('t154');  
                 ## Close the cell  
                 !!!back-token; # <?>  
                 $token = {type => END_TAG_TOKEN, tag_name => $tn};  
                 redo 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                      !!!cp ('t155');                      if ($node->[1] == CAPTION_EL) {
5284                      $i = $_;                        !!!cp ('t155');
5285                      last INSCOPE;                        $i = $_;
5286                    } elsif ({                        last INSCOPE;
5287                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5288                             }->{$node->[1]}) {                        !!!cp ('t156');
5289                      !!!cp ('t156');                        last;
5290                      last INSCOPE;                      }
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) {  
                     !!!cp ('t157');  
 ## TODO: this type is wrong.  
                     !!!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                  while ({                  while ($self->{open_elements}->[-1]->[1]
5304                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5305                    !!!cp ('t158');                    !!!cp ('t158');
5306                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5307                  }                  }
5308    
5309                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5310                    !!!cp ('t159');                    !!!cp ('t159');
5311                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5312                                      text => $self->{open_elements}->[-1]->[0]
5313                                          ->manakai_local_name,
5314                                      token => $token);
5315                  } else {                  } else {
5316                    !!!cp ('t160');                    !!!cp ('t160');
5317                  }                  }
# Line 3904  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');                  !!!cp ('t161');
5330                  #                  #
# Line 3920  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');                      !!!cp ('t163');
5345                      $i = $_;                      $i = $_;
5346                      last INSCOPE;                      last INSCOPE;
5347                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5348                      !!!cp ('t164');                      !!!cp ('t164');
5349                      last INSCOPE;                      last INSCOPE;
5350                    }                    }
5351                  } # INSCOPE                  } # INSCOPE
5352                    unless (defined $i) {                    unless (defined $i) {
5353                      !!!cp ('t165');                      !!!cp ('t165');
5354                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!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                  while ({                  while ($self->{open_elements}->[-1]->[1]
5364                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5365                    !!!cp ('t166');                    !!!cp ('t166');
5366                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5367                  }                  }
5368    
5369                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5370                            ne $token->{tag_name}) {
5371                    !!!cp ('t167');                    !!!cp ('t167');
5372                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5373                                      text => $self->{open_elements}->[-1]->[0]
5374                                          ->manakai_local_name,
5375                                      token => $token);
5376                  } else {                  } else {
5377                    !!!cp ('t168');                    !!!cp ('t168');
5378                  }                  }
# Line 3961  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                  !!!cp ('t169');                  !!!cp ('t169');
5390                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!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');                  !!!cp ('t170');
5397                  #                  #
# Line 3976  sub _tree_construction_main ($) { Line 5400  sub _tree_construction_main ($) {
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                      !!!cp ('t171');                      if ($node->[1] == CAPTION_EL) {
5407                      $i = $_;                        !!!cp ('t171');
5408                      last INSCOPE;                        $i = $_;
5409                    } elsif ({                        last INSCOPE;
5410                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5411                             }->{$node->[1]}) {                        !!!cp ('t172');
5412                      !!!cp ('t172');                        last;
5413                      last INSCOPE;                      }
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) {  
                     !!!cp ('t173');  
                     !!!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                  while ({                  while ($self->{open_elements}->[-1]->[1]
5426                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5427                    !!!cp ('t174');                    !!!cp ('t174');
5428                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5429                  }                  }
5430                                    
5431                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5432                    !!!cp ('t175');                    !!!cp ('t175');
5433                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5434                                      text => $self->{open_elements}->[-1]->[0]
5435                                          ->manakai_local_name,
5436                                      token => $token);
5437                  } else {                  } else {
5438                    !!!cp ('t176');                    !!!cp ('t176');
5439                  }                  }
# Line 4019  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                  !!!cp ('t177');                  !!!cp ('t177');
5451                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!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');                  !!!cp ('t178');
5458                  #                  #
# Line 4038  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                    !!!cp ('t179');                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5472                    $i = $_;                      !!!cp ('t179');
5473                    last INSCOPE;                      $i = $_;
5474                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
5475                    !!!cp ('t180');                      ## Close the cell
5476                    $tn = $node->[1];                      !!!back-token; # </x>
5477                    ## NOTE: There is exactly one |td| or |th| element                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5478                    ## in scope in the stack of open elements by definition.                                line => $token->{line},
5479                  } elsif ({                                column => $token->{column}};
5480                            table => 1, html => 1,                      next B;
5481                           }->{$node->[1]}) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
5482                    !!!cp ('t181');                      !!!cp ('t180');
5483                    last INSCOPE;                      $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  
               unless (defined $i) {  
5493                  !!!cp ('t182');                  !!!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                } else {                } # INSCOPE
                 !!!cp ('t183');  
               }  
   
               ## 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');                    !!!cp ('t184');
5512                    $i = $_;                    $i = $_;
5513                    last INSCOPE;                    last INSCOPE;
5514                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5515                    !!!cp ('t185');                    !!!cp ('t185');
5516                    last INSCOPE;                    last INSCOPE;
5517                  }                  }
5518                } # INSCOPE                } # INSCOPE
5519                unless (defined $i) {                unless (defined $i) {
5520                  !!!cp ('t186');                  !!!cp ('t186');
5521                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!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                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5530                  !!!cp ('t187');                  !!!cp ('t187');
5531                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5532                }                }
5533    
5534                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5535                  !!!cp ('t188');                  !!!cp ('t188');
5536                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5537                                    text => $self->{open_elements}->[-1]->[0]
5538                                        ->manakai_local_name,
5539                                    token => $token);
5540                } else {                } else {
5541                  !!!cp ('t189');                  !!!cp ('t189');
5542                }                }
# Line 4120  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                  !!!cp ('t190');                  !!!cp ('t190');
5557                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!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');                  !!!cp ('t191');
5564                  #                  #
# Line 4140  sub _tree_construction_main ($) { Line 5569  sub _tree_construction_main ($) {
5569                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5570                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5571                !!!cp ('t192');                !!!cp ('t192');
5572                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!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');                !!!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 4156  sub _tree_construction_main ($) { Line 5597  sub _tree_construction_main ($) {
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              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if (not $open_tables->[-1]->[1] and # tainted
5601                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5602              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5603                                
5604                unless (length $token->{data}) {            unless (length $token->{data}) {
5605                  !!!cp ('t194');              !!!cp ('t194');
5606                  !!!next-token;              !!!next-token;
5607                  redo B;              next B;
5608                } else {            } else {
5609                  !!!cp ('t195');              !!!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                      !!!cp ('t196');                  $prev_sibling = $next_sibling->previous_sibling;
5631                      $foster_parent_element = $parent;                  #
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
5632                } else {                } else {
5633                  !!!cp ('t199');                  !!!cp ('t197');
5634                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5635                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
5636                     $next_sibling);                  #
5637                }                }
5638              } else {                last OE;
               !!!cp ('t200');  
               $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                    !!!cp ('t201');                !!!cp ('t201');
5681                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                pop @{$self->{open_elements}};
5682                    pop @{$self->{open_elements}};              }
5683                  }              
5684                                !!!insert-element ('tbody',, $token);
5685                  !!!insert-element ('tbody');              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5686                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              ## reprocess in the "in table body" insertion mode...
5687                  ## reprocess in the "in table body" insertion mode...            }
5688                }            
5689              if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5690                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {              unless ($token->{tag_name} eq 'tr') {
5691                  unless ($token->{tag_name} eq 'tr') {                !!!cp ('t202');
5692                    !!!cp ('t202');                !!!parse-error (type => 'missing start tag:tr', token => $token);
5693                    !!!parse-error (type => 'missing start tag:tr');              }
                 }  
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                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5700                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5701                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              }
                   pop @{$self->{open_elements}};  
                 }  
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                    !!!cp ('t204');                !!!cp ('t204');
5706                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5707                    !!!next-token;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5708                    redo B;                !!!nack ('t204');
5709                  } else {                !!!next-token;
5710                    !!!cp ('t205');                next B;
5711                    !!!insert-element ('tr');              } else {
5712                    ## reprocess in the "in row" insertion mode                !!!cp ('t205');
5713                  }                !!!insert-element ('tr',, $token);
5714                } else {                ## reprocess in the "in row" insertion mode
5715                  !!!cp ('t206');              }
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)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5723                  !!!cp ('t207');                  !!!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') {  
                     !!!cp ('t208');  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ({  
                             html => 1,  
   
                             ## NOTE: This element does not appear here, maybe.  
                             table => 1,  
                            }->{$node->[1]}) {  
                     !!!cp ('t209');  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                  !!!cp ('t210');  
 ## TODO: This type is wrong.  
                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});  
                   ## Ignore the token  
                   !!!next-token;  
                   redo B;  
                 }  
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)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5770                    !!!cp ('t211');                    !!!cp ('t211');
5771                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5772                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5773                  }                  }
5774                                    
# Line 4335  sub _tree_construction_main ($) { Line 5777  sub _tree_construction_main ($) {
5777                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5778                    !!!cp ('t212');                    !!!cp ('t212');
5779                    ## reprocess                    ## reprocess
5780                    redo B;                    !!!ack-later;
5781                      next B;
5782                  } else {                  } else {
5783                    !!!cp ('t213');                    !!!cp ('t213');
5784                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4347  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) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5794                      !!!cp ('t214');                      !!!cp ('t214');
5795                      $i = $_;                      $i = $_;
5796                      last INSCOPE;                      last INSCOPE;
5797                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5798                      !!!cp ('t215');                      !!!cp ('t215');
5799                      last INSCOPE;                      last INSCOPE;
5800                    }                    }
5801                  } # INSCOPE                  } # INSCOPE
5802                  unless (defined $i) {                  unless (defined $i) {
5803                    !!!cp ('t216');                    !!!cp ('t216');
5804  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5805                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!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)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5816                    !!!cp ('t217');                    !!!cp ('t217');
5817                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5818                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5819                  }                  }
5820                                    
# Line 4393  sub _tree_construction_main ($) { Line 5832  sub _tree_construction_main ($) {
5832                  !!!cp ('t218');                  !!!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                    !!!cp ('t219');                !!!cp ('t219');
5840                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
5841                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                pop @{$self->{open_elements}};
5842                    pop @{$self->{open_elements}};              }
5843                  }              
5844                                !!!insert-element ('colgroup',, $token);
5845                  !!!insert-element ('colgroup');              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5846                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              ## reprocess
5847                  ## reprocess              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5848                  redo B;              !!!ack-later;
5849                } elsif ({              next B;
5850                          caption => 1,            } elsif ({
5851                          colgroup => 1,                      caption => 1,
5852                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
5853                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
5854                  ## Clear back to table context                     }->{$token->{tag_name}}) {
5855                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              ## Clear back to table context
5856                         $self->{open_elements}->[-1]->[1] ne 'html') {                  while (not ($self->{open_elements}->[-1]->[1]
5857                                    & TABLE_SCOPING_EL)) {
5858                    !!!cp ('t220');                    !!!cp ('t220');
5859                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
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                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5883                                  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');                    !!!cp ('t221');
5894                    $i = $_;                    $i = $_;
5895                    last INSCOPE;                    last INSCOPE;
5896                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5897                    !!!cp ('t222');                    !!!cp ('t222');
5898                    last INSCOPE;                    last INSCOPE;
5899                  }                  }
# Line 4460  sub _tree_construction_main ($) { Line 5901  sub _tree_construction_main ($) {
5901                unless (defined $i) {                unless (defined $i) {
5902                  !!!cp ('t223');                  !!!cp ('t223');
5903  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5904                  !!!parse-error (type => 'unmatched end tag:table');                  !!!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                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5915                  !!!cp ('t224');                  !!!cp ('t224');
5916                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5917                }                }
5918    
5919                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
5920                  !!!cp ('t225');                  !!!cp ('t225');
5921  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5922                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5923                                    text => $self->{open_elements}->[-1]->[0]
5924                                        ->manakai_local_name,
5925                                    token => $token);
5926                } else {                } else {
5927                  !!!cp ('t226');                  !!!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 {
5984                  !!!cp ('t227.1');
5985                  #
5986                }
5987              } else {
5988                !!!cp ('t227.4');
5989                #
5990              }
5991          } else {          } else {
5992            !!!cp ('t227');            !!!cp ('t227');
           !!!parse-error (type => 'in table:'.$token->{tag_name});  
   
           $insert = $insert_to_foster;  
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) {        } 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) {
# Line 4502  sub _tree_construction_main ($) { Line 6005  sub _tree_construction_main ($) {
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');                    !!!cp ('t228');
6010                    $i = $_;                    $i = $_;
6011                    last INSCOPE;                    last INSCOPE;
6012                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6013                    !!!cp ('t229');                    !!!cp ('t229');
6014                    last INSCOPE;                    last INSCOPE;
6015                  }                  }
6016                } # INSCOPE                } # INSCOPE
6017                unless (defined $i) {                unless (defined $i) {
6018                  !!!cp ('t230');                  !!!cp ('t230');
6019                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!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 {                } else {
6026                  !!!cp ('t232');                  !!!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)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6032                  !!!cp ('t231');                  !!!cp ('t231');
6033  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
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 4544  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');                      !!!cp ('t233');
6051                      $i = $_;                      $i = $_;
6052                      last INSCOPE;                      last INSCOPE;
6053                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6054                      !!!cp ('t234');                      !!!cp ('t234');
6055                      last INSCOPE;                      last INSCOPE;
6056                    }                    }
# Line 4558  sub _tree_construction_main ($) { Line 6058  sub _tree_construction_main ($) {
6058                  unless (defined $i) {                  unless (defined $i) {
6059                    !!!cp ('t235');                    !!!cp ('t235');
6060  ## TODO: The following is wrong.  ## TODO: The following is wrong.
6061                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!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)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6072                    !!!cp ('t236');                    !!!cp ('t236');
6073  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6074                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6075                  }                  }
6076                                    
# Line 4584  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) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
6088                      !!!cp ('t237');                      !!!cp ('t237');
6089                      $i = $_;                      $i = $_;
6090                      last INSCOPE;                      last INSCOPE;
6091                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6092                      !!!cp ('t238');                      !!!cp ('t238');
6093                      last INSCOPE;                      last INSCOPE;
6094                    }                    }
6095                  } # INSCOPE                  } # INSCOPE
6096                  unless (defined $i) {                  unless (defined $i) {
6097                    !!!cp ('t239');                    !!!cp ('t239');
6098                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!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)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6109                    !!!cp ('t240');                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6110                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6111                  }                  }
6112                                    
# Line 4626  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');                    !!!cp ('t241');
6136                    $i = $_;                    $i = $_;
6137                    last INSCOPE;                    last INSCOPE;
6138                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6139                    !!!cp ('t242');                    !!!cp ('t242');
6140                    last INSCOPE;                    last INSCOPE;
6141                  }                  }
6142                } # INSCOPE                } # INSCOPE
6143                unless (defined $i) {                unless (defined $i) {
6144                  !!!cp ('t243');                  !!!cp ('t243');
6145                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!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  
               while ({  
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!cp ('t244');  
 ## ISSUE: Can this case be reached?  
                 pop @{$self->{open_elements}};  
               }  
                 
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!cp ('t245');  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               } else {  
                 !!!cp ('t246');  
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 4680  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');                      !!!cp ('t247');
6171                      $i = $_;                      $i = $_;
6172                      last INSCOPE;                      last INSCOPE;
6173                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6174                      !!!cp ('t248');                      !!!cp ('t248');
6175                      last INSCOPE;                      last INSCOPE;
6176                    }                    }
6177                  } # INSCOPE                  } # INSCOPE
6178                    unless (defined $i) {                    unless (defined $i) {
6179                      !!!cp ('t249');                      !!!cp ('t249');
6180                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!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 4704  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');                      !!!cp ('t250');
6195                      $i = $_;                      $i = $_;
6196                      last INSCOPE;                      last INSCOPE;
6197                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6198                      !!!cp ('t251');                      !!!cp ('t251');
6199                      last INSCOPE;                      last INSCOPE;
6200                    }                    }
6201                  } # INSCOPE                  } # INSCOPE
6202                    unless (defined $i) {                    unless (defined $i) {
6203                      !!!cp ('t252');                      !!!cp ('t252');
6204                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!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)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6215                    !!!cp ('t253');                    !!!cp ('t253');
6216  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6217                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6218                  }                  }
6219                                    
# Line 4742  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');                    !!!cp ('t254');
6231                    $i = $_;                    $i = $_;
6232                    last INSCOPE;                    last INSCOPE;
6233                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6234                    !!!cp ('t255');                    !!!cp ('t255');
6235                    last INSCOPE;                    last INSCOPE;
6236                  }                  }
6237                } # INSCOPE                } # INSCOPE
6238                unless (defined $i) {                unless (defined $i) {
6239                  !!!cp ('t256');                  !!!cp ('t256');
6240                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!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)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6251                  !!!cp ('t257');                  !!!cp ('t257');
6252  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
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                !!!cp ('t258');            !!!cp ('t258');
6268                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6269                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6270                !!!next-token;            ## Ignore the token
6271                redo B;            !!!nack ('t258.1');
6272               !!!next-token;
6273              next B;
6274          } else {          } else {
6275            !!!cp ('t259');            !!!cp ('t259');
6276            !!!parse-error (type => 'in table:/'.$token->{tag_name});            !!!parse-error (type => 'in table:/',
6277                              text => $token->{tag_name}, token => $token);
6278    
6279            $insert = $insert_to_foster;            $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            ## Stop parsing
6294            last B;
6295        } else {        } else {
6296          die "$0: $token->{type}: Unknown token type";          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');                  !!!cp ('t260');
6304                  !!!next-token;                  !!!next-token;
6305                  redo B;                  next B;
6306                }                }
6307              }              }
6308                            
# Line 4812  sub _tree_construction_main ($) { Line 6311  sub _tree_construction_main ($) {
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                !!!cp ('t262');                !!!cp ('t262');
6314                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!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');                !!!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                  !!!cp ('t264');                  !!!cp ('t264');
6327                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!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');                  !!!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                !!!cp ('t266');                !!!cp ('t266');
6341                !!!parse-error (type => 'unmatched end tag:col');                !!!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');                !!!cp ('t267');
6348                #                #
6349              }              }
6350            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6351              die "$0: $token->{type}: Unknown token type";          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              !!!cp ('t269');              !!!cp ('t269');
6371              !!!parse-error (type => 'unmatched end tag:colgroup');  ## 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');              !!!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} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6387        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6388          !!!cp ('t271');          !!!cp ('t271');
6389          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6390          !!!next-token;          !!!next-token;
6391          redo B;          next B;
6392        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6393              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6394                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6395                  !!!cp ('t272');              !!!cp ('t272');
6396                  ## As if </option>              ## As if </option>
6397                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6398                } else {            } else {
6399                  !!!cp ('t273');              !!!cp ('t273');
6400                }            }
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                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6408                  ## As if </option>              !!!cp ('t274');
6409                  pop @{$self->{open_elements}};              ## As if </option>
6410                } else {              pop @{$self->{open_elements}};
6411                  !!!cp ('t275');            } else {
6412                }              !!!cp ('t275');
6413              }
6414    
6415                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6416                  !!!cp ('t276');              !!!cp ('t276');
6417                  ## As if </optgroup>              ## As if </optgroup>
6418                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6419                } else {            } else {
6420                  !!!cp ('t277');              !!!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  ## TODO: The type below is not good - <select> is replaced by </select>          } elsif ({
6428                !!!parse-error (type => 'not closed:select');                     select => 1, input => 1, textarea => 1,
6429                ## As if </select> instead                   }->{$token->{tag_name}} or
6430                ## have an element in table scope                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6431                my $i;                    {
6432                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                     caption => 1, table => 1,
6433                  my $node = $self->{open_elements}->[$_];                     tbody => 1, tfoot => 1, thead => 1,
6434                  if ($node->[1] eq $token->{tag_name}) {                     tr => 1, td => 1, th => 1,
6435                    !!!cp ('t278');                    }->{$token->{tag_name}})) {
6436                    $i = $_;            ## TODO: The type below is not good - <select> is replaced by </select>
6437                    last INSCOPE;            !!!parse-error (type => 'not closed', text => 'select',
6438                  } elsif ({                            token => $token);
6439                            table => 1, html => 1,            ## NOTE: As if the token were </select> (<select> case) or
6440                           }->{$node->[1]}) {            ## as if there were </select> (otherwise).
6441                    !!!cp ('t279');            ## have an element in table scope
6442                    last INSCOPE;            my $i;
6443                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6444                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6445                unless (defined $i) {              if ($node->[1] == SELECT_EL) {
6446                  !!!cp ('t280');                !!!cp ('t278');
6447                  !!!parse-error (type => 'unmatched end tag:select');                $i = $_;
6448                  ## Ignore the token                last INSCOPE;
6449                  !!!next-token;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6450                  redo B;                !!!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                !!!cp ('t281');            !!!cp ('t281');
6465                splice @{$self->{open_elements}}, $i;            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                !!!next-token;
6472                next B;
6473              } else {
6474                !!!cp ('t281.1');
6475                !!!ack-later;
6476                ## Reprocess the token.
6477                next B;
6478              }
6479          } else {          } else {
6480            !!!cp ('t282');            !!!cp ('t282');
6481            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!parse-error (type => 'in select',
6482                              text => $token->{tag_name}, token => $token);
6483            ## Ignore the token            ## Ignore the token
6484              !!!nack ('t282.1');
6485            !!!next-token;            !!!next-token;
6486            redo B;            next B;
6487          }          }
6488        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6489              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6490                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
6491                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
6492                  !!!cp ('t283');              !!!cp ('t283');
6493                  ## As if </option>              ## As if </option>
6494                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6495                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6496                  !!!cp ('t284');              !!!cp ('t284');
6497                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6498                } else {            } else {
6499                  !!!cp ('t285');              !!!cp ('t285');
6500                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6501                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6502                }              ## Ignore the token
6503                !!!next-token;            }
6504                redo B;            !!!nack ('t285.1');
6505              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6506                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6507                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6508                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6509                } else {              !!!cp ('t286');
6510                  !!!cp ('t287');              pop @{$self->{open_elements}};
6511                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            } else {
6512                  ## Ignore the token              !!!cp ('t287');
6513                }              !!!parse-error (type => 'unmatched end tag',
6514                !!!next-token;                              text => $token->{tag_name}, token => $token);
6515                redo B;              ## Ignore the token
6516              } elsif ($token->{tag_name} eq 'select') {            }
6517                ## have an element in table scope            !!!nack ('t287.1');
6518                my $i;            !!!next-token;
6519                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6520                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6521                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6522                    !!!cp ('t288');            my $i;
6523                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6524                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6525                  } elsif ({              if ($node->[1] == SELECT_EL) {
6526                            table => 1, html => 1,                !!!cp ('t288');
6527                           }->{$node->[1]}) {                $i = $_;
6528                    !!!cp ('t289');                last INSCOPE;
6529                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6530                  }                !!!cp ('t289');
6531                } # INSCOPE                last INSCOPE;
6532                unless (defined $i) {              }
6533                  !!!cp ('t290');            } # INSCOPE
6534                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            unless (defined $i) {
6535                  ## Ignore the token              !!!cp ('t290');
6536                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6537                  redo B;                              text => $token->{tag_name}, token => $token);
6538                }              ## Ignore the token
6539                !!!nack ('t290.1');
6540                !!!next-token;
6541                next B;
6542              }
6543                                
6544                !!!cp ('t291');            !!!cp ('t291');
6545                splice @{$self->{open_elements}}, $i;            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                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6556                     }->{$token->{tag_name}}) {
6557  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6558                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6559                              text => $token->{tag_name}, token => $token);
6560                                
6561                ## have an element in table scope            ## have an element in table scope
6562                my $i;            my $i;
6563                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6564                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6565                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6566                    !!!cp ('t292');                !!!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                    !!!cp ('t293');              }
6573                    last INSCOPE;            } # INSCOPE
6574                  }            unless (defined $i) {
6575                } # INSCOPE              !!!cp ('t294');
6576                unless (defined $i) {              ## Ignore the token
6577                  !!!cp ('t294');              !!!nack ('t294.1');
6578                  ## Ignore the token              !!!next-token;
6579                  !!!next-token;              next B;
6580                  redo B;            }
               }  
6581                                
6582                ## As if </select>            ## As if </select>
6583                ## have an element in table scope            ## have an element in table scope
6584                undef $i;            undef $i;
6585                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6586                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6587                  if ($node->[1] eq 'select') {              if ($node->[1] == SELECT_EL) {
6588                    !!!cp ('t295');                !!!cp ('t295');
6589                    $i = $_;                $i = $_;
6590                    last INSCOPE;                last INSCOPE;
6591                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6592  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6593                    !!!cp ('t296');                !!!cp ('t296');
6594                    last INSCOPE;                last INSCOPE;
6595                  }              }
6596                } # INSCOPE            } # INSCOPE
6597                unless (defined $i) {            unless (defined $i) {
6598                  !!!cp ('t297');              !!!cp ('t297');
6599  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6600                  !!!parse-error (type => 'unmatched end tag:select');              !!!parse-error (type => 'unmatched end tag',
6601                  ## Ignore the </select> token                              text => 'select', token => $token);
6602                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6603                  redo B;              !!!nack ('t297.1');
6604                }              !!!next-token; ## TODO: ok?
6605                next B;
6606              }
6607                                
6608                !!!cp ('t298');            !!!cp ('t298');
6609                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6610    
6611                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6612    
6613                ## reprocess            !!!ack-later;
6614                redo B;            ## reprocess
6615              next B;
6616          } else {          } else {
6617            !!!cp ('t299');            !!!cp ('t299');
6618            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!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 {        } else {
6637          die "$0: $token->{type}: Unknown token type";          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 5086  sub _tree_construction_main ($) { Line 6648  sub _tree_construction_main ($) {
6648            unless (length $token->{data}) {            unless (length $token->{data}) {
6649              !!!cp ('t300');              !!!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            !!!cp ('t301');            !!!cp ('t301');
6657            !!!parse-error (type => 'after html:#character');            !!!parse-error (type => 'after html:#text', token => $token);
6658              #
           ## Reprocess in the "after body" insertion mode.  
6659          } else {          } else {
6660            !!!cp ('t302');            !!!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            !!!cp ('t303');            !!!cp ('t303');
6672            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html',
6673                                        text => $token->{tag_name}, token => $token);
6674            ## Reprocess in the "after body" insertion mode.            #
6675          } else {          } else {
6676            !!!cp ('t304');            !!!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            !!!cp ('t305');            !!!cp ('t305');
6690            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!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 "after body" insertion mode.            ## Reprocess.
6695              next B;
6696          } else {          } else {
6697            !!!cp ('t306');            !!!cp ('t306');
6698          }          }
# Line 5136  sub _tree_construction_main ($) { Line 6701  sub _tree_construction_main ($) {
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              !!!cp ('t307');              !!!cp ('t307');
6704              !!!parse-error (type => 'unmatched end tag:html');              !!!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');              !!!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            !!!cp ('t309');            !!!cp ('t309');
6717            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!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');              !!!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              !!!cp ('t311');              !!!cp ('t311');
6746              !!!parse-error (type => 'in frameset:#character');              !!!parse-error (type => 'in frameset:#text', token => $token);
6747            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6748              !!!cp ('t312');              !!!cp ('t312');
6749              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#text', token => $token);
6750            } else { # "after html frameset"            } else { # "after after frameset"
6751              !!!cp ('t313');              !!!cp ('t313');
6752              !!!parse-error (type => 'after html:#character');              !!!parse-error (type => 'after html:#text', token => $token);
   
             $self->{insertion_mode} = AFTER_FRAMESET_IM;  
             ## Reprocess in the "after frameset" insertion mode.  
             !!!parse-error (type => 'after frameset:#character');  
6753            }            }
6754                        
6755            ## Ignore the token.            ## Ignore the token.
# Line 5193  sub _tree_construction_main ($) { Line 6760  sub _tree_construction_main ($) {
6760              !!!cp ('t315');              !!!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) {  
           !!!cp ('t316');  
           !!!parse-error (type => 'after html:'.$token->{tag_name});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t317');  
         }  
   
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            !!!cp ('t318');            !!!cp ('t318');
6771            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!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            !!!cp ('t319');            !!!cp ('t319');
6778            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!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            !!!cp ('t320');            !!!cp ('t320');
6785            ## NOTE: As if in body.            ## NOTE: As if in head.
6786            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6787            redo B;            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              !!!cp ('t321');              !!!cp ('t321');
6794              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!parse-error (type => 'in frameset',
6795            } else {                              text => $token->{tag_name}, token => $token);
6796              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6797              !!!cp ('t322');              !!!cp ('t322');
6798              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!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) {  
           !!!cp ('t323');  
           !!!parse-error (type => 'after html:/'.$token->{tag_name});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t324');  
         }  
   
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              !!!cp ('t325');              !!!cp ('t325');
6816              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!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 {
# Line 5264  sub _tree_construction_main ($) { Line 6824  sub _tree_construction_main ($) {
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');              !!!cp ('t327');
6829              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6830            } else {            } else {
6831              !!!cp ('t328');              !!!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');            !!!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              !!!cp ('t330');              !!!cp ('t330');
6843              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!parse-error (type => 'in frameset:/',
6844            } else {                              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');              !!!cp ('t331');
6851              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!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 5303  sub _tree_construction_main ($) { Line 6878  sub _tree_construction_main ($) {
6878        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
6879          !!!cp ('t332');          !!!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');          !!!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');          !!!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          my $meta_el = 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            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6905              !!!cp ('t335');              !!!cp ('t335');
6906                ## NOTE: Whether the encoding is supported or not is handled
6907                ## in the {change_encoding} callback.
6908              $self->{change_encoding}              $self->{change_encoding}
6909                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6910                            
6911              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6912                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6913                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6914                                           ->{has_reference});                                           ->{has_reference});
6915            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6916              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6917                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6918                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6919                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6920                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6921                       /x) {
6922                !!!cp ('t336');                !!!cp ('t336');
6923                  ## NOTE: Whether the encoding is supported or not is handled
6924                  ## in the {change_encoding} callback.
6925                $self->{change_encoding}                $self->{change_encoding}
6926                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6927                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6928                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6929                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 5367  sub _tree_construction_main ($) { Line 6947  sub _tree_construction_main ($) {
6947            }            }
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          !!!cp ('t341');          !!!cp ('t341');
         !!!parse-error (type => 'in body:title');  
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;
             !!!cp ('t339');  
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             !!!cp ('t340');  
             $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');            !!!cp ('t342');
6964            ## Ignore the token            ## Ignore the token
6965          } else {          } else {
# Line 5401  sub _tree_construction_main ($) { Line 6973  sub _tree_construction_main ($) {
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,  
6982                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6983                  listing => 1, menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6984                  pre => 1,                  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              !!!cp ('t344');              !!!cp ('t344');
7010              !!!back-token;              !!!back-token; # <form>
7011              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7012              redo B;                        line => $token->{line}, column => $token->{column}};
7013            } elsif ({              next B;
7014                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7015              !!!cp ('t345');              !!!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//;
# Line 5440  sub _tree_construction_main ($) { Line 7032  sub _tree_construction_main ($) {
7032            } else {            } else {
7033              !!!cp ('t348');              !!!cp ('t348');
7034            }            }
7035          } else {          } elsif ($token->{tag_name} eq 'form') {
7036            !!!cp ('t347');            !!!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            !!!cp ('t350');            $self->{insertion_mode} = IN_TABLE_IM;
7046            !!!parse-error (type => 'in form:form');  
7047            ## Ignore the token            !!!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') {  
               !!!cp ('t351');  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
               redo B;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               !!!cp ('t352');  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
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              !!!cp ('t353');            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7065              !!!back-token;            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7066              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7067              redo B;            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7068            } elsif ({            ## object (Fx)
7069                      table => 1, caption => 1, td => 1, th => 1,            ## Generate non-tree (non-conforming)
7070                      button => 1, marquee => 1, object => 1, html => 1,            ## basefont (IE7 (where basefont is non-void)), center (IE),
7071                     }->{$_->[1]}) {            ## form (IE), hn (IE)
7072              !!!cp ('t354');          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7073              last INSCOPE;            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7074            }            ## div (Fx, S)
7075          } # INSCOPE  
7076                      my $non_optional;
         ## Step 1  
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                !!!cp ('t355');              {
7084                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7085                                $self->{open_elements}->[-1]->[1]);                #
7086              } else {  
7087                !!!cp ('t356');                ## 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            } else {            } elsif (
7108                       ## NOTE: not "formatting" and not "phrasing"
7109                       ($node->[1] & SPECIAL_EL or
7110                        $node->[1] & SCOPING_EL) and
7111                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7112                       (not $node->[1] & ADDRESS_DIV_P_EL)
7113                      ) {
7114                ## 3.
7115              !!!cp ('t357');              !!!cp ('t357');
7116            }              last; ## goto 5.
7117                        } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
7118              !!!cp ('t358');              !!!cp ('t358');
7119              last LI;              #
7120              } else {
7121                !!!cp ('t359');
7122                $non_optional ||= $node;
7123                #
7124            }            }
7125                        ## 4.
7126            !!!cp ('t359');            ## goto 2.
           ## Step 4  
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              !!!cp ('t360');              !!!cp ('t353');
7134              !!!back-token;  
7135              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              ## NOTE: |<p><li>|, for example.
7136              redo B;  
7137            } elsif ({              !!!back-token; # <x>
7138                      table => 1, caption => 1, td => 1, th => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7139                      button => 1, marquee => 1, object => 1, html => 1,                        line => $token->{line}, column => $token->{column}};
7140                     }->{$_->[1]}) {              next B;
7141              !!!cp ('t361');            } 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                !!!cp ('t362');              {
7164                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7165                                $self->{open_elements}->[-1]->[1]);                #
7166              } else {  
7167                !!!cp ('t363');                ## 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                       ($node->[1] & SPECIAL_EL or
7190                        $node->[1] & SCOPING_EL) and
7191                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7192    
7193                       (not $node->[1] & ADDRESS_DIV_P_EL)
7194                      ) {
7195                ## 3.
7196                !!!cp ('t357.1');
7197                last; ## goto 5.
7198              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7199                !!!cp ('t358.1');
7200                #
7201            } else {            } else {
7202              !!!cp ('t364');              !!!cp ('t359.1');
7203            }              $non_optional ||= $node;
7204                          #
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             !!!cp ('t365');  
             last LI;  
7205            }            }
7206                        ## 4.
7207            !!!cp ('t366');            ## goto 2.
           ## Step 4  
7208            $i--;            $i--;
7209            $node = $self->{open_elements}->[$i];          }
7210            redo LI;  
7211          } # LI          ## 5. (a) has a |p| element in scope
7212                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7213          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            if ($_->[1] == P_EL) {
7214                !!!cp ('t353.1');
7215                !!!back-token; # <x>
7216                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7217                          line => $token->{line}, column => $token->{column}};
7218                next B;
7219              } elsif ($_->[1] & SCOPING_EL) {
7220                !!!cp ('t354.1');
7221                last INSCOPE;
7222              }
7223            } # INSCOPE
7224    
7225            ## 5. (b) insert
7226            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7227            !!!nack ('t359.2');
7228          !!!next-token;          !!!next-token;
7229          redo B;          next B;
7230        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7231            ## NOTE: As normal, but effectively ends parsing
7232    
7233          ## has a p element in scope          ## has a p element in scope
7234          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7235            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7236              !!!cp ('t367');              !!!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) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7242              !!!cp ('t368');              !!!cp ('t368');
7243              last INSCOPE;              last INSCOPE;
7244            }            }
7245          } # INSCOPE          } # INSCOPE
7246                        
7247          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7248                        
7249          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $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              !!!cp ('t371');              !!!cp ('t371');
7259              !!!parse-error (type => 'in a:a');              !!!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]) {
# Line 5643  sub _tree_construction_main ($) { Line 7286  sub _tree_construction_main ($) {
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}}) {  
         !!!cp ('t375');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         redo B;  
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              !!!cp ('t376');              !!!cp ('t376');
7303              !!!parse-error (type => 'in nobr:nobr');              !!!parse-error (type => 'in nobr:nobr', token => $token);
7304              !!!back-token;              !!!back-token; # <nobr>
7305              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7306              redo B;                        line => $token->{line}, column => $token->{column}};
7307            } elsif ({              next B;
7308                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7309              !!!cp ('t377');              !!!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              !!!cp ('t378');              !!!cp ('t378');
7326              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button', token => $token);
7327              !!!back-token;              !!!back-token; # <button>
7328              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7329              redo B;                        line => $token->{line}, column => $token->{column}};
7330            } elsif ({              next B;
7331                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7332              !!!cp ('t379');              !!!cp ('t379');
7333              last INSCOPE;              last INSCOPE;
7334            }            }
# Line 5708  sub _tree_construction_main ($) { Line 7336  sub _tree_construction_main ($) {
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    
7341          ## TODO: associate with $self->{form_element} if defined          ## TODO: associate with $self->{form_element} if defined
7342    
7343          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7344    
7345            !!!nack ('t379.1');
7346          !!!next-token;          !!!next-token;
7347          redo B;          next B;
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         !!!cp ('t380');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         !!!cp ('t381');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $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') {  
             !!!cp ('t382');  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             !!!cp ('t383');  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = IN_TABLE_IM;  
             
         !!!next-token;  
         redo 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            !!!cp ('t384');            !!!cp ('t381');
7357            !!!parse-error (type => 'image');            $reconstruct_active_formatting_elements->($insert_to_current);
           $token->{tag_name} = 'img';  
7358          } else {          } else {
7359            !!!cp ('t385');            !!!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') {  
             !!!cp ('t386');  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             !!!cp ('t387');  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'input') {  
         !!!cp ('t388');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
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');            !!!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 5825  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              !!!cp ('t390');              !!!cp ('t390');
7396              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7397                               #line => $token->{line}, column => $token->{column},
7398                              };
7399            } else {            } else {
7400              !!!cp ('t391');              !!!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              !!!cp ('t392');          ## has an |option| element in scope
7449              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7450            } else {            my $node = $self->{open_elements}->[$_];
7451              !!!cp ('t393');            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          } else {          } # INSCOPE
7463            !!!cp ('t394');  
7464          }          $reconstruct_active_formatting_elements->($insert_to_current);
7465          while ($token->{type} == CHARACTER_TOKEN) {  
7466            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7467            $text .= $token->{data};  
7468            !!!next-token;          !!!nack ('t397.3');
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
         }  
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          !!!cp ('t399');              !!!cp ('t398.1');
7478          ## NOTE: There is an "as if in body" code clone.              ## generate implied end tags
7479          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);              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          !!!cp ('t400');                 $token->{tag_name} eq 'svg') {
7506          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
7507    
7508          ## TODO: associate with $self->{form_element} if defined          ## "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          $self->{insertion_mode} = IN_SELECT_IM;          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7515            
7516            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          !!!cp ('t401');          !!!cp ('t401');
7536          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!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          !!!cp ('t402');          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                !!!cp ('t403');              } elsif ($_->[1] & SCOPING_EL) {
7621                !!!parse-error (type => 'not closed:'.$_->[1]);                !!!cp ('t405.1');
7622              } else {                last;
               !!!cp ('t404');  
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            !!!cp ('t405');            ## NOTE: Ignore the token.
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## 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                $self->{open_elements}->[1]->[1] == BODY_EL) {
7655              unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
7656              !!!cp ('t406');              !!!cp ('t406');
7657              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!parse-error (type => 'not closed',
7658                                text => $self->{open_elements}->[1]->[0]
7659                                    ->manakai_local_name,
7660                                token => $token);
7661            } else {            } else {
7662              !!!cp ('t407');              !!!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            !!!cp ('t408');            !!!cp ('t408');
7669            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!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}) {
             ## generate implied end tags  
             while ({  
                     dd => ($token->{tag_name} ne 'dd'),  
                     dt => ($token->{tag_name} ne 'dt'),  
                     li => ($token->{tag_name} ne 'li'),  
                     p => ($token->{tag_name} ne 'p'),  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!cp ('t409');  
               pop @{$self->{open_elements}};  
             }  
               
7699              !!!cp ('t410');              !!!cp ('t410');
7700              $i = $_;              $i = $_;
7701              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE;
7702            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7703              !!!cp ('t411');              !!!cp ('t411');
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 => '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');              !!!cp ('t412');
7734              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7735                                text => $self->{open_elements}->[-1]->[0]
7736                                    ->manakai_local_name,
7737                                token => $token);
7738            } else {            } else {
7739              !!!cp ('t413');              !!!cp ('t414');
             !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
7740            }            }
7741          }  
7742                      ## Step 3.
         if (defined $i) {  
           !!!cp ('t414');  
7743            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7744          } elsif ($token->{tag_name} eq 'p') {  
7745            !!!cp ('t415');            ## Step 4.
7746            ## As if <p>, then reprocess the current token            $clear_up_to_marker->()
7747            my $el;                if {
7748            !!!create-element ($el, 'p');                  applet => 1, button => 1, marquee => 1, object => 1,
7749            $insert->($el);                }->{$token->{tag_name}};
         } else {  
           !!!cp ('t416');  
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) {
             ## generate implied end tags  
             while ({  
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!cp ('t417');  
               pop @{$self->{open_elements}};  
             }  
   
7763              !!!cp ('t418');              !!!cp ('t418');
7764                $i = $_;
7765              last INSCOPE;              last INSCOPE;
7766            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7767              !!!cp ('t419');              !!!cp ('t419');
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
           !!!cp ('t420');  
           pop @{$self->{open_elements}};  
         } else {  
7773            !!!cp ('t421');            !!!cp ('t421');
7774            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7775                              text => $token->{tag_name}, token => $token);
7776              ## NOTE: Ignore the token.
7777            } else {
7778              ## 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) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             while ({  
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!cp ('t422');  
               pop @{$self->{open_elements}};  
             }  
   
7811              !!!cp ('t423');              !!!cp ('t423');
7812              $i = $_;              $i = $_;
7813              last INSCOPE;              last INSCOPE;
7814            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7815              !!!cp ('t424');              !!!cp ('t424');
7816              last INSCOPE;              last INSCOPE;
7817            }            }
7818          } # INSCOPE          } # INSCOPE
7819    
7820            unless (defined $i) { # has an element in scope
7821              !!!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                    
7846          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          !!!next-token;
7847            !!!cp ('t425');          next B;
7848            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});        } 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 {          } else {
7891            !!!cp ('t426');            !!!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            
         splice @{$self->{open_elements}}, $i if defined $i;  
7903          !!!next-token;          !!!next-token;
7904          redo B;          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          !!!cp ('t427');          !!!cp ('t427');
7912          $formatting_end_tag->($token->{tag_name});          $formatting_end_tag->($token);
7913          redo B;          next B;
7914        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7915          !!!cp ('t428');          !!!cp ('t428');
7916          !!!parse-error (type => 'unmatched end tag:br');          !!!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}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         redo B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
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              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7946                !!!cp ('t430');                !!!cp ('t430');
7947                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7948                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7949                  ## which seems wrong.
7950                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7951                  $node_i++;
7952              }              }
7953                    
7954              ## Step 2              ## Step 2
7955              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7956                    = $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');                !!!cp ('t431');
7960                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7961                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7962                                  text => $self->{open_elements}->[-1]->[0]
7963                                      ->manakai_local_name,
7964                                  token => $token);
7965              } else {              } else {
7966                !!!cp ('t432');                !!!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                !!!cp ('t433');                !!!cp ('t433');
7981                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!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');              !!!cp ('t434');
7994            }            }
7995                        
# Line 6222  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    
8028    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6233  sub _tree_construction_main ($) { Line 8030  sub _tree_construction_main ($) {
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?    ## ISSUE: Should {confident} be true?
8041    
# Line 6256  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 6271  sub set_inner_html ($$$) { Line 8071  sub set_inner_html ($$$) {
8071    
8072      ## Step 8 # 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_char} = 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_char}};        my $char = '';
8083        unshift @{$self->{prev_char}}, $self->{next_char};        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        $self->{next_char} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8113        $self->{next_char} = ord substr $$s, $i++, 1;        $p->{column}++;
8114        $column++;  
8115          if ($self->{nc} == 0x000A) { # LF
8116        if ($self->{next_char} == 0x000A) { # LF          $p->{line}++;
8117          $line++;          $p->{column} = 0;
         $column = 0;  
8118          !!!cp ('i1');          !!!cp ('i1');
8119        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8120          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8121          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8122          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8123          $column = 0;            $self->{next_nc} = $next;
8124            }
8125            $self->{nc} = 0x000A; # LF # MUST
8126            $p->{line}++;
8127            $p->{column} = 0;
8128          !!!cp ('i2');          !!!cp ('i2');
8129        } elsif ($self->{next_char} > 0x10FFFF) {        } elsif ($self->{nc} == 0x0000) { # NULL
         $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
         !!!cp ('i3');  
       } elsif ($self->{next_char} == 0x0000) { # NULL  
8130          !!!cp ('i4');          !!!cp ('i4');
8131          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8132          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8133        }        }
8134      };      };
8135      $p->{prev_char} = [-1, -1, -1];  
8136      $p->{next_char} = -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    
# Line 6334  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 3      ## Step 3
8218      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6344  sub set_inner_html ($$$) { Line 8222  sub set_inner_html ($$$) {
8222      $doc->append_child ($root);      $doc->append_child ($root);
8223    
8224      ## Step 5 # 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 6 # MUST      ## Step 6 # MUST
8231      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
# Line 6390  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    }    }

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24