/[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.100 by wakaba, Sun Mar 9 04:08:41 2008 UTC revision 1.202 by wakaba, Sat Oct 4 14:31:28 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)  require IO::Handle;
23  ## TODO: 1252 parse error (revision 1264)  
24  ## TODO: 8859-11 = 874 (revision 1271)  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  my $permitted_slash_tag_name = {  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    base => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    link => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    meta => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    hr => 1,  
31    br => 1,  sub A_EL () { 0b1 }
32    img => 1,  sub ADDRESS_EL () { 0b10 }
33    embed => 1,  sub BODY_EL () { 0b100 }
34    param => 1,  sub BUTTON_EL () { 0b1000 }
35    area => 1,  sub CAPTION_EL () { 0b10000 }
36    col => 1,  sub DD_EL () { 0b100000 }
37    input => 1,  sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80    }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83    sub ALL_END_TAG_OPTIONAL_EL () {
84      DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89      ## ISSUE: option, optgroup, rt, rp?
90    
91      BODY_EL |
92      HTML_EL |
93      TABLE_CELL_EL |
94      TABLE_ROW_EL |
95      TABLE_ROW_GROUP_EL
96    }
97    
98    sub SCOPING_EL () {
99      BUTTON_EL |
100      CAPTION_EL |
101      HTML_EL |
102      TABLE_EL |
103      TABLE_CELL_EL |
104      MISC_SCOPING_EL
105    }
106    
107    sub TABLE_SCOPING_EL () {
108      HTML_EL |
109      TABLE_EL
110    }
111    
112    sub TABLE_ROWS_SCOPING_EL () {
113      HTML_EL |
114      TABLE_ROW_GROUP_EL
115    }
116    
117    sub TABLE_ROW_SCOPING_EL () {
118      HTML_EL |
119      TABLE_ROW_EL
120    }
121    
122    sub SPECIAL_EL () {
123      ADDRESS_EL |
124      BODY_EL |
125      DIV_EL |
126    
127      DD_EL |
128      DT_EL |
129      LI_EL |
130      P_EL |
131    
132      FORM_EL |
133      FRAMESET_EL |
134      HEADING_EL |
135      SELECT_EL |
136      TABLE_ROW_EL |
137      TABLE_ROW_GROUP_EL |
138      MISC_SPECIAL_EL
139    }
140    
141    my $el_category = {
142      a => A_EL | FORMATTING_EL,
143      address => ADDRESS_EL,
144      applet => MISC_SCOPING_EL,
145      area => MISC_SPECIAL_EL,
146      article => MISC_SPECIAL_EL,
147      aside => MISC_SPECIAL_EL,
148      b => FORMATTING_EL,
149      base => MISC_SPECIAL_EL,
150      basefont => MISC_SPECIAL_EL,
151      bgsound => MISC_SPECIAL_EL,
152      big => FORMATTING_EL,
153      blockquote => MISC_SPECIAL_EL,
154      body => BODY_EL,
155      br => MISC_SPECIAL_EL,
156      button => BUTTON_EL,
157      caption => CAPTION_EL,
158      center => MISC_SPECIAL_EL,
159      col => MISC_SPECIAL_EL,
160      colgroup => MISC_SPECIAL_EL,
161      command => MISC_SPECIAL_EL,
162      datagrid => MISC_SPECIAL_EL,
163      dd => DD_EL,
164      details => MISC_SPECIAL_EL,
165      dialog => MISC_SPECIAL_EL,
166      dir => MISC_SPECIAL_EL,
167      div => DIV_EL,
168      dl => MISC_SPECIAL_EL,
169      dt => DT_EL,
170      em => FORMATTING_EL,
171      embed => MISC_SPECIAL_EL,
172      eventsource => MISC_SPECIAL_EL,
173      fieldset => MISC_SPECIAL_EL,
174      figure => MISC_SPECIAL_EL,
175      font => FORMATTING_EL,
176      footer => MISC_SPECIAL_EL,
177      form => FORM_EL,
178      frame => MISC_SPECIAL_EL,
179      frameset => FRAMESET_EL,
180      h1 => HEADING_EL,
181      h2 => HEADING_EL,
182      h3 => HEADING_EL,
183      h4 => HEADING_EL,
184      h5 => HEADING_EL,
185      h6 => HEADING_EL,
186      head => MISC_SPECIAL_EL,
187      header => MISC_SPECIAL_EL,
188      hr => MISC_SPECIAL_EL,
189      html => HTML_EL,
190      i => FORMATTING_EL,
191      iframe => MISC_SPECIAL_EL,
192      img => MISC_SPECIAL_EL,
193      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194      input => MISC_SPECIAL_EL,
195      isindex => MISC_SPECIAL_EL,
196      li => LI_EL,
197      link => MISC_SPECIAL_EL,
198      listing => MISC_SPECIAL_EL,
199      marquee => MISC_SCOPING_EL,
200      menu => MISC_SPECIAL_EL,
201      meta => MISC_SPECIAL_EL,
202      nav => MISC_SPECIAL_EL,
203      nobr => NOBR_EL | FORMATTING_EL,
204      noembed => MISC_SPECIAL_EL,
205      noframes => MISC_SPECIAL_EL,
206      noscript => MISC_SPECIAL_EL,
207      object => MISC_SCOPING_EL,
208      ol => MISC_SPECIAL_EL,
209      optgroup => OPTGROUP_EL,
210      option => OPTION_EL,
211      p => P_EL,
212      param => MISC_SPECIAL_EL,
213      plaintext => MISC_SPECIAL_EL,
214      pre => MISC_SPECIAL_EL,
215      rp => RUBY_COMPONENT_EL,
216      rt => RUBY_COMPONENT_EL,
217      ruby => RUBY_EL,
218      s => FORMATTING_EL,
219      script => MISC_SPECIAL_EL,
220      select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222      small => FORMATTING_EL,
223      spacer => MISC_SPECIAL_EL,
224      strike => FORMATTING_EL,
225      strong => FORMATTING_EL,
226      style => MISC_SPECIAL_EL,
227      table => TABLE_EL,
228      tbody => TABLE_ROW_GROUP_EL,
229      td => TABLE_CELL_EL,
230      textarea => MISC_SPECIAL_EL,
231      tfoot => TABLE_ROW_GROUP_EL,
232      th => TABLE_CELL_EL,
233      thead => TABLE_ROW_GROUP_EL,
234      title => MISC_SPECIAL_EL,
235      tr => TABLE_ROW_EL,
236      tt => FORMATTING_EL,
237      u => FORMATTING_EL,
238      ul => MISC_SPECIAL_EL,
239      wbr => MISC_SPECIAL_EL,
240    };
241    
242    my $el_category_f = {
243      $MML_NS => {
244        'annotation-xml' => MML_AXML_EL,
245        mi => FOREIGN_FLOW_CONTENT_EL,
246        mo => FOREIGN_FLOW_CONTENT_EL,
247        mn => FOREIGN_FLOW_CONTENT_EL,
248        ms => FOREIGN_FLOW_CONTENT_EL,
249        mtext => FOREIGN_FLOW_CONTENT_EL,
250      },
251      $SVG_NS => {
252        foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253        desc => FOREIGN_FLOW_CONTENT_EL,
254        title => FOREIGN_FLOW_CONTENT_EL,
255      },
256      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257    };
258    
259    my $svg_attr_name = {
260      attributename => 'attributeName',
261      attributetype => 'attributeType',
262      basefrequency => 'baseFrequency',
263      baseprofile => 'baseProfile',
264      calcmode => 'calcMode',
265      clippathunits => 'clipPathUnits',
266      contentscripttype => 'contentScriptType',
267      contentstyletype => 'contentStyleType',
268      diffuseconstant => 'diffuseConstant',
269      edgemode => 'edgeMode',
270      externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322  };  };
323    
324  my $c1_entity_char = {  my $foreign_attr_xname = {
325      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332      'xml:base' => [$XML_NS, ['xml', 'base']],
333      'xml:lang' => [$XML_NS, ['xml', 'lang']],
334      'xml:space' => [$XML_NS, ['xml', 'space']],
335      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337    };
338    
339    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341    my $charref_map = {
342      0x0D => 0x000A,
343    0x80 => 0x20AC,    0x80 => 0x20AC,
344    0x81 => 0xFFFD,    0x81 => 0xFFFD,
345    0x82 => 0x201A,    0x82 => 0x201A,
# Line 59  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
386    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
387    
388  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
389      my $self = shift;
390      my $charset_name = shift;
391      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    } # parse_byte_string
394    
395    sub parse_byte_stream ($$$$;$$) {
396      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
398    my $charset = shift;    my $charset_name = shift;
399    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
   my $s;  
     
   if (defined $charset) {  
     require Encode; ## TODO: decode(utf8) don't delete BOM  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = lc $charset; ## TODO: normalize name  
     $self->{confident} = 1;  
   } else {  
     ## TODO: Implement HTML5 detection algorithm  
     require Whatpm::Charset::UniversalCharDet;  
     $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string  
         (substr ($$bytes_s, 0, 1024));  
     $charset ||= 'windows-1252';  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = $charset;  
     $self->{confident} = 0;  
   }  
400    
401    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
402      my $self = shift;      my (%opt) = @_;
403      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
404      ## TODO: if $charset is supported    };
405      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      ## Step 1        };
410      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
411        $charset = 'utf-8';    ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444      }      }
445    
446      ## Step 2      ## Step 2
447      if (defined $self->{input_encoding} and      my $byte_buffer = '';
448          $self->{input_encoding} eq $charset) {      for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460        $self->{confident} = 1;        $self->{confident} = 1;
461        return;        last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476      }      }
477    
478      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
479          ':'.$charset, level => 'w');      ## TODO: <meta charset>
480    
481      ## Step 3      ## Step 5
482      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
483    
484      ## Step 4      ## Step 6
485      throw Whatpm::HTML::RestartParser (charset => $charset);      require Whatpm::Charset::UniversalCharDet;
486        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487            ($byte_buffer);
488        if (defined $charset_name) {
489          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
491          require Whatpm::Charset::DecodeHandle;
492          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493              ($byte_stream);
494          ($char_stream, $e_status) = $charset->get_decode_handle
495              ($buffer, allow_error_reporting => 1,
496               allow_fallback => 1, byte_buffer => \$byte_buffer);
497          if ($char_stream) {
498            $buffer->{buffer} = $byte_buffer;
499            !!!parse-error (type => 'sniffing:chardet',
500                            text => $charset_name,
501                            level => $self->{level}->{info},
502                            layer => 'encode',
503                            line => 1, column => 1);
504            $self->{confident} = 0;
505            last SNIFFING;
506          }
507        }
508    
509        ## Step 7: default
510        ## TODO: Make this configurable.
511        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513            ## detectable in the step 6.
514        require Whatpm::Charset::DecodeHandle;
515        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516            ($byte_stream);
517        ($char_stream, $e_status)
518            = $charset->get_decode_handle ($buffer,
519                                           allow_error_reporting => 1,
520                                           allow_fallback => 1,
521                                           byte_buffer => \$byte_buffer);
522        $buffer->{buffer} = $byte_buffer;
523        !!!parse-error (type => 'sniffing:default',
524                        text => 'windows-1252',
525                        level => $self->{level}->{info},
526                        line => 1, column => 1,
527                        layer => 'encode');
528        $self->{confident} = 0;
529      } # SNIFFING
530    
531      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533        !!!parse-error (type => 'chardecode:fallback',
534                        #text => $self->{input_encoding},
535                        level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        layer => 'encode');
538      } elsif (not ($e_status &
539                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540        $self->{input_encoding} = $charset->get_iana_name;
541        !!!parse-error (type => 'chardecode:no error',
542                        text => $self->{input_encoding},
543                        level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548      }
549    
550      $self->{change_encoding} = sub {
551        my $self = shift;
552        $charset_name = shift;
553        my $token = shift;
554    
555        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556        ($char_stream, $e_status) = $charset->get_decode_handle
557            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558             byte_buffer => \ $buffer->{buffer});
559        
560        if ($char_stream) { # if supported
561          ## "Change the encoding" algorithm:
562    
563          ## Step 1    
564          if ($charset->{category} &
565              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567            ($char_stream, $e_status) = $charset->get_decode_handle
568                ($byte_stream,
569                 byte_buffer => \ $buffer->{buffer});
570          }
571          $charset_name = $charset->get_iana_name;
572          
573          ## Step 2
574          if (defined $self->{input_encoding} and
575              $self->{input_encoding} eq $charset_name) {
576            !!!parse-error (type => 'charset label:matching',
577                            text => $charset_name,
578                            level => $self->{level}->{info});
579            $self->{confident} = 1;
580            return;
581          }
582    
583          !!!parse-error (type => 'charset label detected',
584                          text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588          
589          ## Step 3
590          # if (can) {
591            ## change the encoding on the fly.
592            #$self->{confident} = 1;
593            #return;
594          # }
595          
596          ## Step 4
597          throw Whatpm::HTML::RestartParser ();
598        }
599    }; # $self->{change_encoding}    }; # $self->{change_encoding}
600    
601    my @args = @_; shift @args; # $s    my $char_onerror = sub {
602        my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604                        line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606        if ($opt{octets}) {
607          ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        }
609      };
610    
611      my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615    my $return;    my $return;
616    try {    try {
617      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
619      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
620      $s = \ (Encode::decode ($charset, $$bytes_s));      
621      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623          !!!parse-error (type => 'chardecode:fallback',
624                          level => $self->{level}->{uncertain},
625                          #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          layer => 'encode');
628        } elsif (not ($e_status &
629                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630          $self->{input_encoding} = $charset->get_iana_name;
631          !!!parse-error (type => 'chardecode:no error',
632                          text => $self->{input_encoding},
633                          level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638        }
639      $self->{confident} = 1;      $self->{confident} = 1;
640      $return = $self->parse_char_string ($s, @args);  
641        $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645    };    };
646    return $return;    return $return;
647  } # parse_byte_string  } # parse_byte_stream
648    
649  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650  ## and the HTML layer MUST ignore it.  However, we does strip BOM in  ## and the HTML layer MUST ignore it.  However, we does strip BOM in
# Line 162  sub parse_byte_string ($$$$;$) { Line 655  sub parse_byte_string ($$$$;$) {
655  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
656  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
657    
658  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
659      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661      my $s = ref $_[0] ? $_[0] : \($_[0]);
662      require Whatpm::Charset::DecodeHandle;
663      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664      return $self->parse_char_stream ($input, @_[1..$#_]);
665    } # parse_char_string
666    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
669    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
670    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
673    
# Line 175  sub parse_string ($$$;$) { Line 676  sub parse_string ($$$;$) {
676    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
677    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
678        if defined $self->{input_encoding};        if defined $self->{input_encoding};
679    ## TODO: |{input_encoding}| is needless?
680    
681    my $i = 0;    $self->{line_prev} = $self->{line} = 1;
682    my $line = 1;    $self->{column_prev} = -1;
683    my $column = 0;    $self->{column} = 0;
684    $self->{set_next_char} = sub {    $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686    
687      pop @{$self->{prev_char}};      my $char = '';
688      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
689          $char = $self->{next_nc};
690          delete $self->{next_nc};
691          $self->{nc} = ord $char;
692        } else {
693          $self->{char_buffer} = '';
694          $self->{char_buffer_pos} = 0;
695    
696          my $count = $input->manakai_read_until
697             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698          if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707          if ($input->read ($char, 1)) {
708            $self->{nc} = ord $char;
709          } else {
710            $self->{nc} = -1;
711            return;
712          }
713        }
714    
715      $self->{next_char} = -1 and return if $i >= length $$s;      ($self->{line_prev}, $self->{column_prev})
716      $self->{next_char} = ord substr $$s, $i++, 1;          = ($self->{line}, $self->{column});
717      $column++;      $self->{column}++;
718            
719      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
720        $line++;        !!!cp ('j1');
721        $column = 0;        $self->{line}++;
722      } elsif ($self->{next_char} == 0x000D) { # CR        $self->{column} = 0;
723        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
724        $self->{next_char} = 0x000A; # LF # MUST        !!!cp ('j2');
725        $line++;  ## TODO: support for abort/streaming
726        $column = 0;        my $next = '';
727      } elsif ($self->{next_char} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
728        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
729      } elsif ($self->{next_char} == 0x0000) { # NULL        }
730          $self->{nc} = 0x000A; # LF # MUST
731          $self->{line}++;
732          $self->{column} = 0;
733        } elsif ($self->{nc} == 0x0000) { # NULL
734          !!!cp ('j4');
735        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
736        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      }      }
738    };    };
739    $self->{prev_char} = [-1, -1, -1];  
740    $self->{next_char} = -1;    $self->{read_until} = sub {
741        #my ($scalar, $specials_range, $offset) = @_;
742        return 0 if defined $self->{next_nc};
743    
744        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745        my $offset = $_[2] || 0;
746    
747        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750            substr ($_[0], $offset)
751                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752            my $count = $+[0] - $-[0];
753            if ($count) {
754              $self->{column} += $count;
755              $self->{char_buffer_pos} += $count;
756              $self->{line_prev} = $self->{line};
757              $self->{column_prev} = $self->{column} - 1;
758              $self->{nc} = -1;
759            }
760            return $count;
761          } else {
762            return 0;
763          }
764        } else {
765          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766          if ($count) {
767            $self->{column} += $count;
768            $self->{line_prev} = $self->{line};
769            $self->{column_prev} = $self->{column} - 1;
770            $self->{nc} = -1;
771          }
772          return $count;
773        }
774      }; # $self->{read_until}
775    
776    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
777      my (%opt) = @_;      my (%opt) = @_;
778      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780        warn "Parse error ($opt{type}) at line $line column $column\n";
781    };    };
782    $self->{parse_error} = sub {    $self->{parse_error} = sub {
783      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
784    };    };
785    
786      my $char_onerror = sub {
787        my (undef, $type, %opt) = @_;
788        !!!parse-error (layer => 'encode',
789                        line => $self->{line}, column => $self->{column} + 1,
790                        %opt, type => $type);
791      }; # $char_onerror
792    
793      if ($_[3]) {
794        $input = $_[3]->($input);
795        $input->onerror ($char_onerror);
796      } else {
797        $input->onerror ($char_onerror) unless defined $input->onerror;
798      }
799    
800    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
801    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
802    $self->_construct_tree;    $self->_construct_tree;
803    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
804    
805      delete $self->{parse_error}; # remove loop
806    
807    return $self->{document};    return $self->{document};
808  } # parse_string  } # parse_char_stream
809    
810  sub new ($) {  sub new ($) {
811    my $class = shift;    my $class = shift;
812    my $self = bless {}, $class;    my $self = bless {
813    $self->{set_next_char} = sub {      level => {must => 'm',
814      $self->{next_char} = -1;                should => 's',
815                  warn => 'w',
816                  info => 'i',
817                  uncertain => 'u'},
818      }, $class;
819      $self->{set_nc} = sub {
820        $self->{nc} = -1;
821    };    };
822    $self->{parse_error} = sub {    $self->{parse_error} = sub {
823      #      #
# Line 254  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 844  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
844  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
847  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
848  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
849  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
850  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 265  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 855  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
855  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
861  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 287  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 877  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
877  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
879  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880    sub SELF_CLOSING_START_TAG_STATE () { 34 }
881    sub CDATA_SECTION_STATE () { 35 }
882    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890    ## NOTE: "Entity data state", "entity in attribute value state", and
891    ## "consume a character reference" algorithm are jointly implemented
892    ## using the following six states:
893    sub ENTITY_STATE () { 44 }
894    sub ENTITY_HASH_STATE () { 45 }
895    sub NCR_NUM_STATE () { 46 }
896    sub HEXREF_X_STATE () { 47 }
897    sub HEXREF_HEX_STATE () { 48 }
898    sub ENTITY_NAME_STATE () { 49 }
899    sub PCDATA_STATE () { 50 } # "data state" in the spec
900    
901  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
902  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 303  sub TABLE_IMS ()      { 0b1000000 } Line 913  sub TABLE_IMS ()      { 0b1000000 }
913  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
914  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
915  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
916    sub SELECT_IMS ()     { 0b10000000000 }
917    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918        ## NOTE: "in foreign content" insertion mode is special; it is combined
919        ## with the secondary insertion mode.  In this parser, they are stored
920        ## together in the bit-or'ed form.
921    
922  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
923    
# Line 325  sub IN_TABLE_IM () { TABLE_IMS } Line 940  sub IN_TABLE_IM () { TABLE_IMS }
940  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
941  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
942  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
943  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
944    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
945  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
946    
947  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
# Line 333  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 949  sub IN_COLUMN_GROUP_IM () { 0b10 }
949  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
950    my $self = shift;    my $self = shift;
951    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
952      #$self->{s_kwd}; # state keyword - initialized when used
953      #$self->{entity__value}; # initialized when used
954      #$self->{entity__match}; # initialized when used
955    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
956    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
957    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
958    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
959    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
960    $self->{char} = [];    delete $self->{self_closing};
961    # $self->{next_char}    $self->{char_buffer} = '';
962      $self->{char_buffer_pos} = 0;
963      $self->{nc} = -1; # next input character
964      #$self->{next_nc}
965    !!!next-input-character;    !!!next-input-character;
966    $self->{token} = [];    $self->{token} = [];
967    # $self->{escape}    # $self->{escape}
# Line 350  sub _initialize_tokenizer ($) { Line 972  sub _initialize_tokenizer ($) {
972  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
973  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
974  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
975  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
976  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
977  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
978  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
979  ##        ->{name}  ##        ->{name}
980  ##        ->{value}  ##        ->{value}
981  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
982  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
983    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
984    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
985    ##     while the token is pushed back to the stack.
986    
987  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
988    
# Line 367  sub _initialize_tokenizer ($) { Line 992  sub _initialize_tokenizer ($) {
992  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
993  ## and removed from the list.  ## and removed from the list.
994    
995  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
996  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
997  ## contains some requirements that are not detected by the  
998  ## parsing algorithm:  my $is_space = {
999  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1000  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1001  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1002  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1003  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1004  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1005    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1006    
1007  sub _get_next_token ($) {  sub _get_next_token ($) {
1008    my $self = shift;    my $self = shift;
1009    
1010      if ($self->{self_closing}) {
1011        !!!parse-error (type => 'nestc', token => $self->{ct});
1012        ## NOTE: The |self_closing| flag is only set by start tag token.
1013        ## In addition, when a start tag token is emitted, it is always set to
1014        ## |ct|.
1015        delete $self->{self_closing};
1016      }
1017    
1018    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1019        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1020      return shift @{$self->{token}};      return shift @{$self->{token}};
1021    }    }
1022    
1023    A: {    A: {
1024      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1025        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1026    
1027          if ($self->{nc} == 0x0026) { # &
1028            !!!cp (0.1);
1029            ## NOTE: In the spec, the tokenizer is switched to the
1030            ## "entity data state".  In this implementation, the tokenizer
1031            ## is switched to the |ENTITY_STATE|, which is an implementation
1032            ## of the "consume a character reference" algorithm.
1033            $self->{entity_add} = -1;
1034            $self->{prev_state} = DATA_STATE;
1035            $self->{state} = ENTITY_STATE;
1036            !!!next-input-character;
1037            redo A;
1038          } elsif ($self->{nc} == 0x003C) { # <
1039            !!!cp (0.2);
1040            $self->{state} = TAG_OPEN_STATE;
1041            !!!next-input-character;
1042            redo A;
1043          } elsif ($self->{nc} == -1) {
1044            !!!cp (0.3);
1045            !!!emit ({type => END_OF_FILE_TOKEN,
1046                      line => $self->{line}, column => $self->{column}});
1047            last A; ## TODO: ok?
1048          } else {
1049            !!!cp (0.4);
1050            #
1051          }
1052    
1053          # Anything else
1054          my $token = {type => CHARACTER_TOKEN,
1055                       data => chr $self->{nc},
1056                       line => $self->{line}, column => $self->{column},
1057                      };
1058          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1059    
1060          ## Stay in the state.
1061          !!!next-input-character;
1062          !!!emit ($token);
1063          redo A;
1064        } elsif ($self->{state} == DATA_STATE) {
1065          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1066          if ($self->{nc} == 0x0026) { # &
1067            $self->{s_kwd} = '';
1068          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1069              not $self->{escape}) {              not $self->{escape}) {
1070            !!!cp (1);            !!!cp (1);
1071            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1072              ## "entity data state".  In this implementation, the tokenizer
1073              ## is switched to the |ENTITY_STATE|, which is an implementation
1074              ## of the "consume a character reference" algorithm.
1075              $self->{entity_add} = -1;
1076              $self->{prev_state} = DATA_STATE;
1077              $self->{state} = ENTITY_STATE;
1078            !!!next-input-character;            !!!next-input-character;
1079            redo A;            redo A;
1080          } else {          } else {
1081            !!!cp (2);            !!!cp (2);
1082            #            #
1083          }          }
1084        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1085          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1086            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1087              if ($self->{prev_char}->[0] == 0x002D and # -            
1088                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1089                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1090                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1091                $self->{escape} = 1;              $self->{s_kwd} = '--';
1092              } else {              #
1093                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1094              }              !!!cp (4);
1095                $self->{s_kwd} = '--';
1096                #
1097            } else {            } else {
1098              !!!cp (5);              !!!cp (5);
1099                #
1100            }            }
1101          }          }
1102                    
1103          #          #
1104        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1105            if (length $self->{s_kwd}) {
1106              !!!cp (5.1);
1107              $self->{s_kwd} .= '!';
1108              #
1109            } else {
1110              !!!cp (5.2);
1111              #$self->{s_kwd} = '';
1112              #
1113            }
1114            #
1115          } elsif ($self->{nc} == 0x003C) { # <
1116          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1117              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1118               not $self->{escape})) {               not $self->{escape})) {
# Line 428  sub _get_next_token ($) { Line 1122  sub _get_next_token ($) {
1122            redo A;            redo A;
1123          } else {          } else {
1124            !!!cp (7);            !!!cp (7);
1125              $self->{s_kwd} = '';
1126            #            #
1127          }          }
1128        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1129          if ($self->{escape} and          if ($self->{escape} and
1130              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1131            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1132              !!!cp (8);              !!!cp (8);
1133              delete $self->{escape};              delete $self->{escape};
1134            } else {            } else {
# Line 444  sub _get_next_token ($) { Line 1138  sub _get_next_token ($) {
1138            !!!cp (10);            !!!cp (10);
1139          }          }
1140                    
1141            $self->{s_kwd} = '';
1142          #          #
1143        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1144          !!!cp (11);          !!!cp (11);
1145          !!!emit ({type => END_OF_FILE_TOKEN});          $self->{s_kwd} = '';
1146            !!!emit ({type => END_OF_FILE_TOKEN,
1147                      line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149        } else {        } else {
1150          !!!cp (12);          !!!cp (12);
1151            $self->{s_kwd} = '';
1152            #
1153        }        }
1154    
1155        # Anything else        # Anything else
1156        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1157                     data => chr $self->{next_char}};                     data => chr $self->{nc},
1158        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1159        !!!next-input-character;                    };
1160          if ($self->{read_until}->($token->{data}, q[-!<>&],
1161        !!!emit ($token);                                  length $token->{data})) {
1162            $self->{s_kwd} = '';
1163        redo A;        }
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
1164    
1165        unless (defined $token) {        ## Stay in the data state.
1166          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167          !!!cp (13);          !!!cp (13);
1168          !!!emit ({type => CHARACTER_TOKEN, data => '&'});          $self->{state} = PCDATA_STATE;
1169        } else {        } else {
1170          !!!cp (14);          !!!cp (14);
1171          !!!emit ($token);          ## Stay in the state.
1172        }        }
1173          !!!next-input-character;
1174          !!!emit ($token);
1175        redo A;        redo A;
1176      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1177        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1178          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1179            !!!cp (15);            !!!cp (15);
1180            !!!next-input-character;            !!!next-input-character;
1181            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1182            redo A;            redo A;
1183            } elsif ($self->{nc} == 0x0021) { # !
1184              !!!cp (15.1);
1185              $self->{s_kwd} = '<' unless $self->{escape};
1186              #
1187          } else {          } else {
1188            !!!cp (16);            !!!cp (16);
1189            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<'});  
   
           redo A;  
1190          }          }
1191    
1192            ## reconsume
1193            $self->{state} = DATA_STATE;
1194            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1195                      line => $self->{line_prev},
1196                      column => $self->{column_prev},
1197                     });
1198            redo A;
1199        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1200          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1201            !!!cp (17);            !!!cp (17);
1202            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1203            !!!next-input-character;            !!!next-input-character;
1204            redo A;            redo A;
1205          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1206            !!!cp (18);            !!!cp (18);
1207            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1208            !!!next-input-character;            !!!next-input-character;
1209            redo A;            redo A;
1210          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1211                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1212            !!!cp (19);            !!!cp (19);
1213            $self->{current_token}            $self->{ct}
1214              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1215                 tag_name => chr ($self->{next_char} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
1216                   line => $self->{line_prev},
1217                   column => $self->{column_prev}};
1218            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1219            !!!next-input-character;            !!!next-input-character;
1220            redo A;            redo A;
1221          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1222                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1223            !!!cp (20);            !!!cp (20);
1224            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1225                              tag_name => chr ($self->{next_char})};                                      tag_name => chr ($self->{nc}),
1226                                        line => $self->{line_prev},
1227                                        column => $self->{column_prev}};
1228            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1229            !!!next-input-character;            !!!next-input-character;
1230            redo A;            redo A;
1231          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1232            !!!cp (21);            !!!cp (21);
1233            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag',
1234                              line => $self->{line_prev},
1235                              column => $self->{column_prev});
1236            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238    
1239            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1240                        line => $self->{line_prev},
1241                        column => $self->{column_prev},
1242                       });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1246            !!!cp (22);            !!!cp (22);
1247            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio',
1248                              line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1251            ## $self->{next_char} is intentionally left as is            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                        line => $self->{line_prev},
1253                                        column => $self->{column_prev},
1254                                       };
1255              ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!cp (23);            !!!cp (23);
1259            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago',
1260                              line => $self->{line_prev},
1261                              column => $self->{column_prev});
1262            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1263            ## reconsume            ## reconsume
1264    
1265            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1266                        line => $self->{line_prev},
1267                        column => $self->{column_prev},
1268                       });
1269    
1270            redo A;            redo A;
1271          }          }
# Line 551  sub _get_next_token ($) { Line 1273  sub _get_next_token ($) {
1273          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1274        }        }
1275      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1276        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1277          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_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;  
1278    
1279                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281                redo A;          if (defined $self->{last_stag_name}) {
1282              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283            }            $self->{s_kwd} = '';
1284            push @next_char, $self->{next_char};            ## Reconsume.
1285                    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...  
           }  
1286          } else {          } else {
1287            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1288              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1289            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1290            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1291            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## Reconsume.
1292              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                        line => $l, column => $c,
1294                       });
1295            redo A;            redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1300            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1301          !!!cp (29);          !!!cp (29);
1302          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct}
1303                            tag_name => chr ($self->{next_char} + 0x0020)};              = {type => END_TAG_TOKEN,
1304                   tag_name => chr ($self->{nc} + 0x0020),
1305                   line => $l, column => $c};
1306          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1307          !!!next-input-character;          !!!next-input-character;
1308          redo A;          redo A;
1309        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1310                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1311          !!!cp (30);          !!!cp (30);
1312          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1313                            tag_name => chr ($self->{next_char})};                                    tag_name => chr ($self->{nc}),
1314                                      line => $l, column => $c};
1315          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1316          !!!next-input-character;          !!!next-input-character;
1317          redo A;          redo A;
1318        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1319          !!!cp (31);          !!!cp (31);
1320          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag',
1321                            line => $self->{line_prev}, ## "<" in "</>"
1322                            column => $self->{column_prev} - 1);
1323          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1327          !!!cp (32);          !!!cp (32);
1328          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1329          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1330          # reconsume          # reconsume
1331    
1332          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1333                      line => $l, column => $c,
1334                     });
1335    
1336          redo A;          redo A;
1337        } else {        } else {
1338          !!!cp (33);          !!!cp (33);
1339          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1340          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1341          ## $self->{next_char} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1343                                      column => $self->{column_prev} - 1,
1344                                     };
1345            ## NOTE: $self->{nc} is intentionally left as is.
1346            ## Although the "anything else" case of the spec not explicitly
1347            ## states that the next input character is to be reconsumed,
1348            ## it will be included to the |data| of the comment token
1349            ## generated from the bogus end tag, as defined in the
1350            ## "bogus comment state" entry.
1351            redo A;
1352          }
1353        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1354          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1355          if (length $ch) {
1356            my $CH = $ch;
1357            $ch =~ tr/a-z/A-Z/;
1358            my $nch = chr $self->{nc};
1359            if ($nch eq $ch or $nch eq $CH) {
1360              !!!cp (24);
1361              ## Stay in the state.
1362              $self->{s_kwd} .= $nch;
1363              !!!next-input-character;
1364              redo A;
1365            } else {
1366              !!!cp (25);
1367              $self->{state} = DATA_STATE;
1368              ## Reconsume.
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375            }
1376          } else { # after "<{tag-name}"
1377            unless ($is_space->{$self->{nc}} or
1378                    {
1379                     0x003E => 1, # >
1380                     0x002F => 1, # /
1381                     -1 => 1, # EOF
1382                    }->{$self->{nc}}) {
1383              !!!cp (26);
1384              ## Reconsume.
1385              $self->{state} = DATA_STATE;
1386              !!!emit ({type => CHARACTER_TOKEN,
1387                        data => '</' . $self->{s_kwd},
1388                        line => $self->{line_prev},
1389                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1390                       });
1391              redo A;
1392            } else {
1393              !!!cp (27);
1394              $self->{ct}
1395                  = {type => END_TAG_TOKEN,
1396                     tag_name => $self->{last_stag_name},
1397                     line => $self->{line_prev},
1398                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1399              $self->{state} = TAG_NAME_STATE;
1400              ## Reconsume.
1401              redo A;
1402            }
1403        }        }
1404      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1405        if ($self->{next_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  
1406          !!!cp (34);          !!!cp (34);
1407          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1408          !!!next-input-character;          !!!next-input-character;
1409          redo A;          redo A;
1410        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1411          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1412            !!!cp (35);            !!!cp (35);
1413            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1414                = 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) {  
1415            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1416            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1417            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1418            #  !!! cp (36);            #  !!! cp (36);
1419            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 670  sub _get_next_token ($) { Line 1421  sub _get_next_token ($) {
1421              !!!cp (37);              !!!cp (37);
1422            #}            #}
1423          } else {          } else {
1424            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1425          }          }
1426          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1427          !!!next-input-character;          !!!next-input-character;
1428    
1429          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1430    
1431          redo A;          redo A;
1432        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1433                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1434          !!!cp (38);          !!!cp (38);
1435          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1436            # start tag or end tag            # start tag or end tag
1437          ## Stay in this state          ## Stay in this state
1438          !!!next-input-character;          !!!next-input-character;
1439          redo A;          redo A;
1440        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1441          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1442          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1443            !!!cp (39);            !!!cp (39);
1444            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445                = 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) {  
1446            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1447            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1448            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1449            #  !!! cp (40);            #  !!! cp (40);
1450            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 703  sub _get_next_token ($) { Line 1452  sub _get_next_token ($) {
1452              !!!cp (41);              !!!cp (41);
1453            #}            #}
1454          } else {          } else {
1455            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1456          }          }
1457          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1458          # reconsume          # reconsume
1459    
1460          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1461    
1462          redo A;          redo A;
1463        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1464            !!!cp (42);
1465            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1466          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_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  
1467          redo A;          redo A;
1468        } else {        } else {
1469          !!!cp (44);          !!!cp (44);
1470          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1471            # start tag or end tag            # start tag or end tag
1472          ## Stay in the state          ## Stay in the state
1473          !!!next-input-character;          !!!next-input-character;
1474          redo A;          redo A;
1475        }        }
1476      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1477        if ($self->{next_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  
1478          !!!cp (45);          !!!cp (45);
1479          ## Stay in the state          ## Stay in the state
1480          !!!next-input-character;          !!!next-input-character;
1481          redo A;          redo A;
1482        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1483          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1484            !!!cp (46);            !!!cp (46);
1485            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486                = 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) {  
1487            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1488            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1489              !!!cp (47);              !!!cp (47);
1490              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1491            } else {            } else {
1492              !!!cp (48);              !!!cp (48);
1493            }            }
1494          } else {          } else {
1495            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1496          }          }
1497          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1498          !!!next-input-character;          !!!next-input-character;
1499    
1500          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1501    
1502          redo A;          redo A;
1503        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1504                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1505          !!!cp (49);          !!!cp (49);
1506          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1507                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1508                   value => '',
1509                   line => $self->{line}, column => $self->{column}};
1510          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1514            !!!cp (50);
1515            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1516          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_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  
1517          redo A;          redo A;
1518        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1519          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1520          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1521            !!!cp (52);            !!!cp (52);
1522            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523                = 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) {  
1524            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1526              !!!cp (53);              !!!cp (53);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528            } else {            } else {
1529              !!!cp (54);              !!!cp (54);
1530            }            }
1531          } else {          } else {
1532            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1533          }          }
1534          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1535          # reconsume          # reconsume
1536    
1537          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1538    
1539          redo A;          redo A;
1540        } else {        } else {
# Line 819  sub _get_next_token ($) { Line 1542  sub _get_next_token ($) {
1542               0x0022 => 1, # "               0x0022 => 1, # "
1543               0x0027 => 1, # '               0x0027 => 1, # '
1544               0x003D => 1, # =               0x003D => 1, # =
1545              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1546            !!!cp (55);            !!!cp (55);
1547            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1548          } else {          } else {
1549            !!!cp (56);            !!!cp (56);
1550          }          }
1551          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{ca}
1552                                value => ''};              = {name => chr ($self->{nc}),
1553                   value => '',
1554                   line => $self->{line}, column => $self->{column}};
1555          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1556          !!!next-input-character;          !!!next-input-character;
1557          redo A;          redo A;
1558        }        }
1559      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1560        my $before_leave = sub {        my $before_leave = sub {
1561          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1562              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1563            !!!cp (57);            !!!cp (57);
1564            !!!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});
1565            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1566          } else {          } else {
1567            !!!cp (58);            !!!cp (58);
1568            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1569              = $self->{current_attribute};              = $self->{ca};
1570          }          }
1571        }; # $before_leave        }; # $before_leave
1572    
1573        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  
1574          !!!cp (59);          !!!cp (59);
1575          $before_leave->();          $before_leave->();
1576          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1577          !!!next-input-character;          !!!next-input-character;
1578          redo A;          redo A;
1579        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1580          !!!cp (60);          !!!cp (60);
1581          $before_leave->();          $before_leave->();
1582          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584          redo A;          redo A;
1585        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1586          $before_leave->();          $before_leave->();
1587          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1588            !!!cp (61);            !!!cp (61);
1589            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590                = 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) {  
1591            !!!cp (62);            !!!cp (62);
1592            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1593            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1594              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1595            }            }
1596          } else {          } else {
1597            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1598          }          }
1599          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1600          !!!next-input-character;          !!!next-input-character;
1601    
1602          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1603    
1604          redo A;          redo A;
1605        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1606                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1607          !!!cp (63);          !!!cp (63);
1608          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1609          ## Stay in the state          ## Stay in the state
1610          !!!next-input-character;          !!!next-input-character;
1611          redo A;          redo A;
1612        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1613            !!!cp (64);
1614          $before_leave->();          $before_leave->();
1615            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1616          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_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  
1617          redo A;          redo A;
1618        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1619          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1620          $before_leave->();          $before_leave->();
1621          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1622            !!!cp (66);            !!!cp (66);
1623            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624                = 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) {  
1625            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1626            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1627              !!!cp (67);              !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629            } else {            } else {
# Line 924  sub _get_next_token ($) { Line 1631  sub _get_next_token ($) {
1631              !!!cp (68);              !!!cp (68);
1632            }            }
1633          } else {          } else {
1634            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1635          }          }
1636          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1637          # reconsume          # reconsume
1638    
1639          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1640    
1641          redo A;          redo A;
1642        } else {        } else {
1643          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1644              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1645            !!!cp (69);            !!!cp (69);
1646            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1647          } else {          } else {
1648            !!!cp (70);            !!!cp (70);
1649          }          }
1650          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1651          ## Stay in the state          ## Stay in the state
1652          !!!next-input-character;          !!!next-input-character;
1653          redo A;          redo A;
1654        }        }
1655      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1656        if ($self->{next_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  
1657          !!!cp (71);          !!!cp (71);
1658          ## Stay in the state          ## Stay in the state
1659          !!!next-input-character;          !!!next-input-character;
1660          redo A;          redo A;
1661        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1662          !!!cp (72);          !!!cp (72);
1663          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1664          !!!next-input-character;          !!!next-input-character;
1665          redo A;          redo A;
1666        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1667          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1668            !!!cp (73);            !!!cp (73);
1669            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670                = 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) {  
1671            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1672            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1673              !!!cp (74);              !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675            } else {            } else {
# Line 976  sub _get_next_token ($) { Line 1677  sub _get_next_token ($) {
1677              !!!cp (75);              !!!cp (75);
1678            }            }
1679          } else {          } else {
1680            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1681          }          }
1682          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1683          !!!next-input-character;          !!!next-input-character;
1684    
1685          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1686    
1687          redo A;          redo A;
1688        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1689                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1690          !!!cp (76);          !!!cp (76);
1691          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1692                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1693                   value => '',
1694                   line => $self->{line}, column => $self->{column}};
1695          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1696          !!!next-input-character;          !!!next-input-character;
1697          redo A;          redo A;
1698        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1699            !!!cp (77);
1700            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1701          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_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  
1702          redo A;          redo A;
1703        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1704          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1705          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1706            !!!cp (79);            !!!cp (79);
1707            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708                = 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) {  
1709            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1710            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1711              !!!cp (80);              !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713            } else {            } else {
# Line 1025  sub _get_next_token ($) { Line 1715  sub _get_next_token ($) {
1715              !!!cp (81);              !!!cp (81);
1716            }            }
1717          } else {          } else {
1718            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1719          }          }
1720          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1721          # reconsume          # reconsume
1722    
1723          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1724    
1725          redo A;          redo A;
1726        } else {        } else {
1727          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1728          $self->{current_attribute} = {name => chr ($self->{next_char}),              $self->{nc} == 0x0027) { # '
1729                                value => ''};            !!!cp (78);
1730              !!!parse-error (type => 'bad attribute name');
1731            } else {
1732              !!!cp (82);
1733            }
1734            $self->{ca}
1735                = {name => chr ($self->{nc}),
1736                   value => '',
1737                   line => $self->{line}, column => $self->{column}};
1738          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1739          !!!next-input-character;          !!!next-input-character;
1740          redo A;                  redo A;        
1741        }        }
1742      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1743        if ($self->{next_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        
1744          !!!cp (83);          !!!cp (83);
1745          ## Stay in the state          ## Stay in the state
1746          !!!next-input-character;          !!!next-input-character;
1747          redo A;          redo A;
1748        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1749          !!!cp (84);          !!!cp (84);
1750          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1751          !!!next-input-character;          !!!next-input-character;
1752          redo A;          redo A;
1753        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1754          !!!cp (85);          !!!cp (85);
1755          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1756          ## reconsume          ## reconsume
1757          redo A;          redo A;
1758        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1759          !!!cp (86);          !!!cp (86);
1760          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1761          !!!next-input-character;          !!!next-input-character;
1762          redo A;          redo A;
1763        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1764          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1765            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1766            !!!cp (87);            !!!cp (87);
1767            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1768                = 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) {  
1769            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1770            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1771              !!!cp (88);              !!!cp (88);
1772              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1773            } else {            } else {
# Line 1082  sub _get_next_token ($) { Line 1775  sub _get_next_token ($) {
1775              !!!cp (89);              !!!cp (89);
1776            }            }
1777          } else {          } else {
1778            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1779          }          }
1780          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1781          !!!next-input-character;          !!!next-input-character;
1782    
1783          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1784    
1785          redo A;          redo A;
1786        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1787          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1788          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1789            !!!cp (90);            !!!cp (90);
1790            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791                = 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) {  
1792            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1793            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1794              !!!cp (91);              !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796            } else {            } else {
# Line 1107  sub _get_next_token ($) { Line 1798  sub _get_next_token ($) {
1798              !!!cp (92);              !!!cp (92);
1799            }            }
1800          } else {          } else {
1801            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1802          }          }
1803          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1804          ## reconsume          ## reconsume
1805    
1806          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1807    
1808          redo A;          redo A;
1809        } else {        } else {
1810          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1811            !!!cp (93);            !!!cp (93);
1812            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1813          } else {          } else {
1814            !!!cp (94);            !!!cp (94);
1815          }          }
1816          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1817          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1818          !!!next-input-character;          !!!next-input-character;
1819          redo A;          redo A;
1820        }        }
1821      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1822        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1823          !!!cp (95);          !!!cp (95);
1824          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1825          !!!next-input-character;          !!!next-input-character;
1826          redo A;          redo A;
1827        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1828          !!!cp (96);          !!!cp (96);
1829          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1830          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1831            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1832            ## implementation of the "consume a character reference" algorithm.
1833            $self->{prev_state} = $self->{state};
1834            $self->{entity_add} = 0x0022; # "
1835            $self->{state} = ENTITY_STATE;
1836          !!!next-input-character;          !!!next-input-character;
1837          redo A;          redo A;
1838        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1839          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1840          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1841            !!!cp (97);            !!!cp (97);
1842            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843                = 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) {  
1844            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1845            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1846              !!!cp (98);              !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848            } else {            } else {
# Line 1156  sub _get_next_token ($) { Line 1850  sub _get_next_token ($) {
1850              !!!cp (99);              !!!cp (99);
1851            }            }
1852          } else {          } else {
1853            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1854          }          }
1855          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1856          ## reconsume          ## reconsume
1857    
1858          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1859    
1860          redo A;          redo A;
1861        } else {        } else {
1862          !!!cp (100);          !!!cp (100);
1863          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1864            $self->{read_until}->($self->{ca}->{value},
1865                                  q["&],
1866                                  length $self->{ca}->{value});
1867    
1868          ## Stay in the state          ## Stay in the state
1869          !!!next-input-character;          !!!next-input-character;
1870          redo A;          redo A;
1871        }        }
1872      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1873        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1874          !!!cp (101);          !!!cp (101);
1875          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1876          !!!next-input-character;          !!!next-input-character;
1877          redo A;          redo A;
1878        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1879          !!!cp (102);          !!!cp (102);
1880          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1881          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1882            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1883            ## implementation of the "consume a character reference" algorithm.
1884            $self->{entity_add} = 0x0027; # '
1885            $self->{prev_state} = $self->{state};
1886            $self->{state} = ENTITY_STATE;
1887          !!!next-input-character;          !!!next-input-character;
1888          redo A;          redo A;
1889        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1890          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1891          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1892            !!!cp (103);            !!!cp (103);
1893            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894                = 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) {  
1895            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1897              !!!cp (104);              !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899            } else {            } else {
# Line 1200  sub _get_next_token ($) { Line 1901  sub _get_next_token ($) {
1901              !!!cp (105);              !!!cp (105);
1902            }            }
1903          } else {          } else {
1904            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1905          }          }
1906          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1907          ## reconsume          ## reconsume
1908    
1909          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1910    
1911          redo A;          redo A;
1912        } else {        } else {
1913          !!!cp (106);          !!!cp (106);
1914          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1915            $self->{read_until}->($self->{ca}->{value},
1916                                  q['&],
1917                                  length $self->{ca}->{value});
1918    
1919          ## Stay in the state          ## Stay in the state
1920          !!!next-input-character;          !!!next-input-character;
1921          redo A;          redo A;
1922        }        }
1923      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1924        if ($self->{next_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  
1925          !!!cp (107);          !!!cp (107);
1926          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1927          !!!next-input-character;          !!!next-input-character;
1928          redo A;          redo A;
1929        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1930          !!!cp (108);          !!!cp (108);
1931          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1932          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1933            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1934            ## implementation of the "consume a character reference" algorithm.
1935            $self->{entity_add} = -1;
1936            $self->{prev_state} = $self->{state};
1937            $self->{state} = ENTITY_STATE;
1938          !!!next-input-character;          !!!next-input-character;
1939          redo A;          redo A;
1940        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1941          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1942            !!!cp (109);            !!!cp (109);
1943            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1944                = 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) {  
1945            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1946            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1947              !!!cp (110);              !!!cp (110);
1948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1949            } else {            } else {
# Line 1247  sub _get_next_token ($) { Line 1951  sub _get_next_token ($) {
1951              !!!cp (111);              !!!cp (111);
1952            }            }
1953          } else {          } else {
1954            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1955          }          }
1956          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1957          !!!next-input-character;          !!!next-input-character;
1958    
1959          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1960    
1961          redo A;          redo A;
1962        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1963          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1964          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1965            !!!cp (112);            !!!cp (112);
1966            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967                = 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) {  
1968            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1969            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1970              !!!cp (113);              !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972            } else {            } else {
# Line 1272  sub _get_next_token ($) { Line 1974  sub _get_next_token ($) {
1974              !!!cp (114);              !!!cp (114);
1975            }            }
1976          } else {          } else {
1977            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1978          }          }
1979          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1980          ## reconsume          ## reconsume
1981    
1982          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1983    
1984          redo A;          redo A;
1985        } else {        } else {
# Line 1285  sub _get_next_token ($) { Line 1987  sub _get_next_token ($) {
1987               0x0022 => 1, # "               0x0022 => 1, # "
1988               0x0027 => 1, # '               0x0027 => 1, # '
1989               0x003D => 1, # =               0x003D => 1, # =
1990              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1991            !!!cp (115);            !!!cp (115);
1992            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1993          } else {          } else {
1994            !!!cp (116);            !!!cp (116);
1995          }          }
1996          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1997            $self->{read_until}->($self->{ca}->{value},
1998                                  q["'=& >],
1999                                  length $self->{ca}->{value});
2000    
2001          ## Stay in the state          ## Stay in the state
2002          !!!next-input-character;          !!!next-input-character;
2003          redo A;          redo A;
2004        }        }
     } 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;  
2005      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2006        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  
2007          !!!cp (118);          !!!cp (118);
2008          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2009          !!!next-input-character;          !!!next-input-character;
2010          redo A;          redo A;
2011        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2012          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2013            !!!cp (119);            !!!cp (119);
2014            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
2015                = 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) {  
2016            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2017            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2018              !!!cp (120);              !!!cp (120);
2019              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2020            } else {            } else {
# Line 1344  sub _get_next_token ($) { Line 2022  sub _get_next_token ($) {
2022              !!!cp (121);              !!!cp (121);
2023            }            }
2024          } else {          } else {
2025            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2026          }          }
2027          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2028          !!!next-input-character;          !!!next-input-character;
2029    
2030          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2031    
2032          redo A;          redo A;
2033        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2034            !!!cp (122);
2035            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2036          !!!next-input-character;          !!!next-input-character;
2037          if ($self->{next_char} == 0x003E and # >          redo A;
2038              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2039              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2040            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2041            !!!cp (122);            !!!cp (122.3);
2042            #            $self->{last_stag_name} = $self->{ct}->{tag_name};
2043            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2044              if ($self->{ct}->{attributes}) {
2045                !!!cp (122.1);
2046                !!!parse-error (type => 'end tag attribute');
2047              } else {
2048                ## NOTE: This state should never be reached.
2049                !!!cp (122.2);
2050              }
2051          } else {          } else {
2052            !!!cp (123);            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!parse-error (type => 'nestc');  
2053          }          }
2054          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2055          # next-input-character is already done          ## Reconsume.
2056            !!!emit ($self->{ct}); # start tag or end tag
2057          redo A;          redo A;
2058        } else {        } else {
2059          !!!cp (124);          !!!cp ('124.1');
2060          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2061          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2062          ## reconsume          ## reconsume
2063          redo A;          redo A;
2064        }        }
2065      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2066        ## (only happen if PCDATA state)        if ($self->{nc} == 0x003E) { # >
2067                  if ($self->{ct}->{type} == END_TAG_TOKEN) {
2068        my $token = {type => COMMENT_TOKEN, data => ''};            !!!cp ('124.2');
2069              !!!parse-error (type => 'nestc', token => $self->{ct});
2070        BC: {            ## TODO: Different type than slash in start tag
2071          if ($self->{next_char} == 0x003E) { # >            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072            !!!cp (124);            if ($self->{ct}->{attributes}) {
2073            $self->{state} = DATA_STATE;              !!!cp ('124.4');
2074            !!!next-input-character;              !!!parse-error (type => 'end tag attribute');
2075              } else {
2076            !!!emit ($token);              !!!cp ('124.5');
2077              }
2078              ## TODO: Test |<title></title/>|
2079            } else {
2080              !!!cp ('124.3');
2081              $self->{self_closing} = 1;
2082            }
2083    
2084            redo A;          $self->{state} = DATA_STATE;
2085          } elsif ($self->{next_char} == -1) {          !!!next-input-character;
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2086    
2087            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2088    
2089            redo A;          redo A;
2090          } elsif ($self->{nc} == -1) {
2091            !!!parse-error (type => 'unclosed tag');
2092            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2093              !!!cp (124.7);
2094              $self->{last_stag_name} = $self->{ct}->{tag_name};
2095            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2096              if ($self->{ct}->{attributes}) {
2097                !!!cp (124.5);
2098                !!!parse-error (type => 'end tag attribute');
2099              } else {
2100                ## NOTE: This state should never be reached.
2101                !!!cp (124.6);
2102              }
2103          } else {          } else {
2104            !!!cp (126);            die "$0: $self->{ct}->{type}: Unknown token type";
           $token->{data} .= chr ($self->{next_char});  
           !!!next-input-character;  
           redo BC;  
2105          }          }
2106        } # BC          $self->{state} = DATA_STATE;
2107            ## Reconsume.
2108        die "$0: _get_next_token: unexpected case [BC]";          !!!emit ($self->{ct}); # start tag or end tag
2109      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {          redo A;
2110          } else {
2111            !!!cp ('124.4');
2112            !!!parse-error (type => 'nestc');
2113            ## TODO: This error type is wrong.
2114            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2115            ## Reconsume.
2116            redo A;
2117          }
2118        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2119        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2120    
2121        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122        push @next_char, $self->{next_char};        ## consumes characters one-by-one basis.
2123                
2124        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2125            !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127          !!!next-input-character;          !!!next-input-character;
2128          push @next_char, $self->{next_char};  
2129          if ($self->{next_char} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2130            !!!cp (127);          redo A;
2131            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};        } elsif ($self->{nc} == -1) {
2132            $self->{state} = COMMENT_START_STATE;          !!!cp (125);
2133            !!!next-input-character;          $self->{state} = DATA_STATE;
2134            redo A;          ## reconsume
2135          } else {  
2136            !!!cp (128);          !!!emit ($self->{ct}); # comment
2137          }          redo A;
2138        } elsif ($self->{next_char} == 0x0044 or # D        } else {
2139                 $self->{next_char} == 0x0064) { # d          !!!cp (126);
2140            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141            $self->{read_until}->($self->{ct}->{data},
2142                                  q[>],
2143                                  length $self->{ct}->{data});
2144    
2145            ## Stay in the state.
2146          !!!next-input-character;          !!!next-input-character;
2147          push @next_char, $self->{next_char};          redo A;
2148          if ($self->{next_char} == 0x004F or # O        }
2149              $self->{next_char} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2150            !!!next-input-character;        ## (only happen if PCDATA state)
2151            push @next_char, $self->{next_char};        
2152            if ($self->{next_char} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2153                $self->{next_char} == 0x0063) { # c          !!!cp (133);
2154              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2155              push @next_char, $self->{next_char};          !!!next-input-character;
2156              if ($self->{next_char} == 0x0054 or # T          redo A;
2157                  $self->{next_char} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2158                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2159                push @next_char, $self->{next_char};          ## ASCII case-insensitive.
2160                if ($self->{next_char} == 0x0059 or # Y          !!!cp (130);
2161                    $self->{next_char} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2162                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2163                  push @next_char, $self->{next_char};          !!!next-input-character;
2164                  if ($self->{next_char} == 0x0050 or # P          redo A;
2165                      $self->{next_char} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2166                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2167                    push @next_char, $self->{next_char};                 $self->{nc} == 0x005B) { # [
2168                    if ($self->{next_char} == 0x0045 or # E          !!!cp (135.4);                
2169                        $self->{next_char} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2170                      !!!cp (129);          $self->{s_kwd} = '[';
2171                      ## TODO: What a stupid code this is!          !!!next-input-character;
2172                      $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);  
         }  
2173        } else {        } else {
2174          !!!cp (136);          !!!cp (136);
2175        }        }
2176    
2177        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2178        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2179        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2180          ## Reconsume.
2181        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2182          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                    line => $self->{line_prev},
2184                                    column => $self->{column_prev} - 1,
2185                                   };
2186        redo A;        redo A;
2187              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2188        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2189        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2190            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191                                      line => $self->{line_prev},
2192                                      column => $self->{column_prev} - 2,
2193                                     };
2194            $self->{state} = COMMENT_START_STATE;
2195            !!!next-input-character;
2196            redo A;
2197          } else {
2198            !!!cp (128);
2199            !!!parse-error (type => 'bogus comment',
2200                            line => $self->{line_prev},
2201                            column => $self->{column_prev} - 2);
2202            $self->{state} = BOGUS_COMMENT_STATE;
2203            ## Reconsume.
2204            $self->{ct} = {type => COMMENT_TOKEN,
2205                                      data => '-',
2206                                      line => $self->{line_prev},
2207                                      column => $self->{column_prev} - 2,
2208                                     };
2209            redo A;
2210          }
2211        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2212          ## ASCII case-insensitive.
2213          if ($self->{nc} == [
2214                undef,
2215                0x004F, # O
2216                0x0043, # C
2217                0x0054, # T
2218                0x0059, # Y
2219                0x0050, # P
2220              ]->[length $self->{s_kwd}] or
2221              $self->{nc} == [
2222                undef,
2223                0x006F, # o
2224                0x0063, # c
2225                0x0074, # t
2226                0x0079, # y
2227                0x0070, # p
2228              ]->[length $self->{s_kwd}]) {
2229            !!!cp (131);
2230            ## Stay in the state.
2231            $self->{s_kwd} .= chr $self->{nc};
2232            !!!next-input-character;
2233            redo A;
2234          } elsif ((length $self->{s_kwd}) == 6 and
2235                   ($self->{nc} == 0x0045 or # E
2236                    $self->{nc} == 0x0065)) { # e
2237            !!!cp (129);
2238            $self->{state} = DOCTYPE_STATE;
2239            $self->{ct} = {type => DOCTYPE_TOKEN,
2240                                      quirks => 1,
2241                                      line => $self->{line_prev},
2242                                      column => $self->{column_prev} - 7,
2243                                     };
2244            !!!next-input-character;
2245            redo A;
2246          } else {
2247            !!!cp (132);        
2248            !!!parse-error (type => 'bogus comment',
2249                            line => $self->{line_prev},
2250                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2251            $self->{state} = BOGUS_COMMENT_STATE;
2252            ## Reconsume.
2253            $self->{ct} = {type => COMMENT_TOKEN,
2254                                      data => $self->{s_kwd},
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2257                                     };
2258            redo A;
2259          }
2260        } elsif ($self->{state} == MD_CDATA_STATE) {
2261          if ($self->{nc} == {
2262                '[' => 0x0043, # C
2263                '[C' => 0x0044, # D
2264                '[CD' => 0x0041, # A
2265                '[CDA' => 0x0054, # T
2266                '[CDAT' => 0x0041, # A
2267              }->{$self->{s_kwd}}) {
2268            !!!cp (135.1);
2269            ## Stay in the state.
2270            $self->{s_kwd} .= chr $self->{nc};
2271            !!!next-input-character;
2272            redo A;
2273          } elsif ($self->{s_kwd} eq '[CDATA' and
2274                   $self->{nc} == 0x005B) { # [
2275            !!!cp (135.2);
2276            $self->{ct} = {type => CHARACTER_TOKEN,
2277                                      data => '',
2278                                      line => $self->{line_prev},
2279                                      column => $self->{column_prev} - 7};
2280            $self->{state} = CDATA_SECTION_STATE;
2281            !!!next-input-character;
2282            redo A;
2283          } else {
2284            !!!cp (135.3);
2285            !!!parse-error (type => 'bogus comment',
2286                            line => $self->{line_prev},
2287                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2288            $self->{state} = BOGUS_COMMENT_STATE;
2289            ## Reconsume.
2290            $self->{ct} = {type => COMMENT_TOKEN,
2291                                      data => $self->{s_kwd},
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2294                                     };
2295            redo A;
2296          }
2297      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2298        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2299          !!!cp (137);          !!!cp (137);
2300          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2301          !!!next-input-character;          !!!next-input-character;
2302          redo A;          redo A;
2303        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2304          !!!cp (138);          !!!cp (138);
2305          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2306          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2307          !!!next-input-character;          !!!next-input-character;
2308    
2309          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2310    
2311          redo A;          redo A;
2312        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2313          !!!cp (139);          !!!cp (139);
2314          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2315          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2316          ## reconsume          ## reconsume
2317    
2318          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2319    
2320          redo A;          redo A;
2321        } else {        } else {
2322          !!!cp (140);          !!!cp (140);
2323          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2324              .= chr ($self->{next_char});              .= chr ($self->{nc});
2325          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2326          !!!next-input-character;          !!!next-input-character;
2327          redo A;          redo A;
2328        }        }
2329      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2330        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2331          !!!cp (141);          !!!cp (141);
2332          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2333          !!!next-input-character;          !!!next-input-character;
2334          redo A;          redo A;
2335        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2336          !!!cp (142);          !!!cp (142);
2337          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2338          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2339          !!!next-input-character;          !!!next-input-character;
2340    
2341          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2342    
2343          redo A;          redo A;
2344        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2345          !!!cp (143);          !!!cp (143);
2346          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2347          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2348          ## reconsume          ## reconsume
2349    
2350          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2351    
2352          redo A;          redo A;
2353        } else {        } else {
2354          !!!cp (144);          !!!cp (144);
2355          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2356              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2357          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2358          !!!next-input-character;          !!!next-input-character;
2359          redo A;          redo A;
2360        }        }
2361      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2362        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2363          !!!cp (145);          !!!cp (145);
2364          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2365          !!!next-input-character;          !!!next-input-character;
2366          redo A;          redo A;
2367        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2368          !!!cp (146);          !!!cp (146);
2369          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2370          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2371          ## reconsume          ## reconsume
2372    
2373          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2374    
2375          redo A;          redo A;
2376        } else {        } else {
2377          !!!cp (147);          !!!cp (147);
2378          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2379            $self->{read_until}->($self->{ct}->{data},
2380                                  q[-],
2381                                  length $self->{ct}->{data});
2382    
2383          ## Stay in the state          ## Stay in the state
2384          !!!next-input-character;          !!!next-input-character;
2385          redo A;          redo A;
2386        }        }
2387      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2388        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2389          !!!cp (148);          !!!cp (148);
2390          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
2393        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2394          !!!cp (149);          !!!cp (149);
2395          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2396          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2397          ## reconsume          ## reconsume
2398    
2399          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2400    
2401          redo A;          redo A;
2402        } else {        } else {
2403          !!!cp (150);          !!!cp (150);
2404          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2405          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2406          !!!next-input-character;          !!!next-input-character;
2407          redo A;          redo A;
2408        }        }
2409      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2410        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2411          !!!cp (151);          !!!cp (151);
2412          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2413          !!!next-input-character;          !!!next-input-character;
2414    
2415          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2416    
2417          redo A;          redo A;
2418        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2419          !!!cp (152);          !!!cp (152);
2420          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2421          $self->{current_token}->{data} .= '-'; # comment                          line => $self->{line_prev},
2422                            column => $self->{column_prev});
2423            $self->{ct}->{data} .= '-'; # comment
2424          ## Stay in the state          ## Stay in the state
2425          !!!next-input-character;          !!!next-input-character;
2426          redo A;          redo A;
2427        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2428          !!!cp (153);          !!!cp (153);
2429          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2430          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2431          ## reconsume          ## reconsume
2432    
2433          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2434    
2435          redo A;          redo A;
2436        } else {        } else {
2437          !!!cp (154);          !!!cp (154);
2438          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2439          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment                          line => $self->{line_prev},
2440                            column => $self->{column_prev});
2441            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2442          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2443          !!!next-input-character;          !!!next-input-character;
2444          redo A;          redo A;
2445        }        }
2446      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2447        if ($self->{next_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  
2448          !!!cp (155);          !!!cp (155);
2449          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2450          !!!next-input-character;          !!!next-input-character;
# Line 1643  sub _get_next_token ($) { Line 2457  sub _get_next_token ($) {
2457          redo A;          redo A;
2458        }        }
2459      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2460        if ($self->{next_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  
2461          !!!cp (157);          !!!cp (157);
2462          ## Stay in the state          ## Stay in the state
2463          !!!next-input-character;          !!!next-input-character;
2464          redo A;          redo A;
2465        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2466          !!!cp (158);          !!!cp (158);
2467          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2468          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2469          !!!next-input-character;          !!!next-input-character;
2470    
2471          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2472    
2473          redo A;          redo A;
2474        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2475          !!!cp (159);          !!!cp (159);
2476          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2477          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2478          ## reconsume          ## reconsume
2479    
2480          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482          redo A;          redo A;
2483        } else {        } else {
2484          !!!cp (160);          !!!cp (160);
2485          $self->{current_token}          $self->{ct}->{name} = chr $self->{nc};
2486              = {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  
2487          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2488          !!!next-input-character;          !!!next-input-character;
2489          redo A;          redo A;
2490        }        }
2491      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2493        if ($self->{next_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  
2494          !!!cp (161);          !!!cp (161);
2495          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2499          !!!cp (162);          !!!cp (162);
2500          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2507          !!!cp (163);          !!!cp (163);
2508          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2509          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2513          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          !!!cp (164);          !!!cp (164);
2518          $self->{current_token}->{name}          $self->{ct}->{name}
2519            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2520          ## Stay in the state          ## Stay in the state
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2525        if ($self->{next_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  
2526          !!!cp (165);          !!!cp (165);
2527          ## Stay in the state          ## Stay in the state
2528          !!!next-input-character;          !!!next-input-character;
2529          redo A;          redo A;
2530        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531          !!!cp (166);          !!!cp (166);
2532          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536    
2537          redo A;          redo A;
2538        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2539          !!!cp (167);          !!!cp (167);
2540          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2541          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2542          ## reconsume          ## reconsume
2543    
2544          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2545          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2546    
2547          redo A;          redo A;
2548        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2549                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552          !!!next-input-character;          !!!next-input-character;
2553          if ($self->{next_char} == 0x0055 or # U          redo A;
2554              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2555            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2556            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2557                $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  
2558          !!!next-input-character;          !!!next-input-character;
2559          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);  
         }  
   
         #  
2560        } else {        } else {
2561          !!!cp (180);          !!!cp (180);
2562            !!!parse-error (type => 'string after DOCTYPE name');
2563            $self->{ct}->{quirks} = 1;
2564    
2565            $self->{state} = BOGUS_DOCTYPE_STATE;
2566          !!!next-input-character;          !!!next-input-character;
2567          #          redo A;
2568        }        }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570          ## ASCII case-insensitive
2571          if ($self->{nc} == [
2572                undef,
2573                0x0055, # U
2574                0x0042, # B
2575                0x004C, # L
2576                0x0049, # I
2577              ]->[length $self->{s_kwd}] or
2578              $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596            redo A;
2597          } else {
2598            !!!cp (169);
2599            !!!parse-error (type => 'string after DOCTYPE name',
2600                            line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2605        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2606            redo A;
2607          }
2608        } elsif ($self->{state} == SYSTEM_STATE) {
2609          ## ASCII case-insensitive
2610          if ($self->{nc} == [
2611                undef,
2612                0x0059, # Y
2613                0x0053, # S
2614                0x0054, # T
2615                0x0045, # E
2616              ]->[length $self->{s_kwd}] or
2617              $self->{nc} == [
2618                undef,
2619                0x0079, # y
2620                0x0073, # s
2621                0x0074, # t
2622                0x0065, # e
2623              ]->[length $self->{s_kwd}]) {
2624            !!!cp (170);
2625            ## Stay in the state.
2626            $self->{s_kwd} .= chr $self->{nc};
2627            !!!next-input-character;
2628            redo A;
2629          } elsif ((length $self->{s_kwd}) == 5 and
2630                   ($self->{nc} == 0x004D or # M
2631                    $self->{nc} == 0x006D)) { # m
2632            !!!cp (171);
2633            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2634            !!!next-input-character;
2635            redo A;
2636          } else {
2637            !!!cp (172);
2638            !!!parse-error (type => 'string after DOCTYPE name',
2639                            line => $self->{line_prev},
2640                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2641            $self->{ct}->{quirks} = 1;
2642    
2643        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2644        # next-input-character is already done          ## Reconsume.
2645        redo A;          redo A;
2646          }
2647      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648        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}}) {  
2649          !!!cp (181);          !!!cp (181);
2650          ## Stay in the state          ## Stay in the state
2651          !!!next-input-character;          !!!next-input-character;
2652          redo A;          redo A;
2653        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2654          !!!cp (182);          !!!cp (182);
2655          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2656          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657          !!!next-input-character;          !!!next-input-character;
2658          redo A;          redo A;
2659        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2660          !!!cp (183);          !!!cp (183);
2661          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2662          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663          !!!next-input-character;          !!!next-input-character;
2664          redo A;          redo A;
2665        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2666          !!!cp (184);          !!!cp (184);
2667          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2668    
2669          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2670          !!!next-input-character;          !!!next-input-character;
2671    
2672          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2673          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2674    
2675          redo A;          redo A;
2676        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2677          !!!cp (185);          !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679    
2680          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2681          ## reconsume          ## reconsume
2682    
2683          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2684          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2685    
2686          redo A;          redo A;
2687        } else {        } else {
2688          !!!cp (186);          !!!cp (186);
2689          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2690          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2691    
2692          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2693          !!!next-input-character;          !!!next-input-character;
2694          redo A;          redo A;
2695        }        }
2696      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2698          !!!cp (187);          !!!cp (187);
2699          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2700          !!!next-input-character;          !!!next-input-character;
2701          redo A;          redo A;
2702        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2703          !!!cp (188);          !!!cp (188);
2704          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2705    
2706          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2707          !!!next-input-character;          !!!next-input-character;
2708    
2709          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2710          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2711    
2712          redo A;          redo A;
2713        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2714          !!!cp (189);          !!!cp (189);
2715          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2718          ## reconsume          ## reconsume
2719    
2720          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2721          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          !!!cp (190);          !!!cp (190);
2726          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2727              .= chr $self->{next_char};              .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731          ## Stay in the state          ## Stay in the state
2732          !!!next-input-character;          !!!next-input-character;
2733          redo A;          redo A;
2734        }        }
2735      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2737          !!!cp (191);          !!!cp (191);
2738          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739          !!!next-input-character;          !!!next-input-character;
2740          redo A;          redo A;
2741        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2742          !!!cp (192);          !!!cp (192);
2743          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2746          !!!next-input-character;          !!!next-input-character;
2747    
2748          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2749          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2750    
2751          redo A;          redo A;
2752        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2753          !!!cp (193);          !!!cp (193);
2754          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2757          ## reconsume          ## reconsume
2758    
2759          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2760          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          !!!cp (194);          !!!cp (194);
2765          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2766              .= chr $self->{next_char};              .= chr $self->{nc};
2767            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2768                                  length $self->{ct}->{pubid});
2769    
2770          ## Stay in the state          ## Stay in the state
2771          !!!next-input-character;          !!!next-input-character;
2772          redo A;          redo A;
2773        }        }
2774      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2776          !!!cp (195);          !!!cp (195);
2777          ## Stay in the state          ## Stay in the state
2778          !!!next-input-character;          !!!next-input-character;
2779          redo A;          redo A;
2780        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2781          !!!cp (196);          !!!cp (196);
2782          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2783          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784          !!!next-input-character;          !!!next-input-character;
2785          redo A;          redo A;
2786        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2787          !!!cp (197);          !!!cp (197);
2788          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2789          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790          !!!next-input-character;          !!!next-input-character;
2791          redo A;          redo A;
2792        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2793          !!!cp (198);          !!!cp (198);
2794          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2795          !!!next-input-character;          !!!next-input-character;
2796    
2797          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2798    
2799          redo A;          redo A;
2800        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2801          !!!cp (199);          !!!cp (199);
2802          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2803    
2804          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2808          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812          !!!cp (200);          !!!cp (200);
2813          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2814          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2815    
2816          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2817          !!!next-input-character;          !!!next-input-character;
2818          redo A;          redo A;
2819        }        }
2820      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2822          !!!cp (201);          !!!cp (201);
2823          ## Stay in the state          ## Stay in the state
2824          !!!next-input-character;          !!!next-input-character;
2825          redo A;          redo A;
2826        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2827          !!!cp (202);          !!!cp (202);
2828          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2829          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830          !!!next-input-character;          !!!next-input-character;
2831          redo A;          redo A;
2832        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2833          !!!cp (203);          !!!cp (203);
2834          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2835          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836          !!!next-input-character;          !!!next-input-character;
2837          redo A;          redo A;
2838        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2839          !!!cp (204);          !!!cp (204);
2840          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2841          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2845          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2849          !!!cp (205);          !!!cp (205);
2850          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2851    
2852          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2853          ## reconsume          ## reconsume
2854    
2855          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2856          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2857    
2858          redo A;          redo A;
2859        } else {        } else {
2860          !!!cp (206);          !!!cp (206);
2861          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2862          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2863    
2864          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2865          !!!next-input-character;          !!!next-input-character;
2866          redo A;          redo A;
2867        }        }
2868      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2870          !!!cp (207);          !!!cp (207);
2871          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872          !!!next-input-character;          !!!next-input-character;
2873          redo A;          redo A;
2874        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2875          !!!cp (208);          !!!cp (208);
2876          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880    
2881          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2882          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2883    
2884          redo A;          redo A;
2885        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2886          !!!cp (209);          !!!cp (209);
2887          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          !!!cp (210);          !!!cp (210);
2898          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2899              .= chr $self->{next_char};              .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909          !!!cp (211);          !!!cp (211);
2910          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911          !!!next-input-character;          !!!next-input-character;
2912          redo A;          redo A;
2913        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2914          !!!cp (212);          !!!cp (212);
2915          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2918          !!!next-input-character;          !!!next-input-character;
2919    
2920          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2921          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2922    
2923          redo A;          redo A;
2924        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2925          !!!cp (213);          !!!cp (213);
2926          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2927    
2928          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2929          ## reconsume          ## reconsume
2930    
2931          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          !!!cp (214);          !!!cp (214);
2937          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2938              .= chr $self->{next_char};              .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942          ## Stay in the state          ## Stay in the state
2943          !!!next-input-character;          !!!next-input-character;
2944          redo A;          redo A;
2945        }        }
2946      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2948          !!!cp (215);          !!!cp (215);
2949          ## Stay in the state          ## Stay in the state
2950          !!!next-input-character;          !!!next-input-character;
2951          redo A;          redo A;
2952        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953          !!!cp (216);          !!!cp (216);
2954          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956    
2957          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2958    
2959          redo A;          redo A;
2960        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2961          !!!cp (217);          !!!cp (217);
2962          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2963          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2964          ## reconsume          ## reconsume
2965    
2966          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2967          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2968    
2969          redo A;          redo A;
2970        } else {        } else {
2971          !!!cp (218);          !!!cp (218);
2972          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2973          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2974    
2975          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2976          !!!next-input-character;          !!!next-input-character;
2977          redo A;          redo A;
2978        }        }
2979      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2981          !!!cp (219);          !!!cp (219);
2982          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2983          !!!next-input-character;          !!!next-input-character;
2984    
2985          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2986    
2987          redo A;          redo A;
2988        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2989          !!!cp (220);          !!!cp (220);
2990          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2991          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2992          ## reconsume          ## reconsume
2993    
2994          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2995    
2996          redo A;          redo A;
2997        } else {        } else {
2998          !!!cp (221);          !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002          ## Stay in the state          ## Stay in the state
3003          !!!next-input-character;          !!!next-input-character;
3004          redo A;          redo A;
3005        }        }
3006      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3008      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3010          
3011    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3012  } # _get_next_token          !!!cp (221.1);
3013            $self->{state} = CDATA_SECTION_MSE1_STATE;
3014            !!!next-input-character;
3015            redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034  sub _tokenize_attempt_to_consume_an_entity ($$$) {          ## Stay in the state.
3035    my ($self, $in_attr, $additional) = @_;          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039    if ({        ## ISSUE: "text tokens" in spec.
3040         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3042         $additional => 1,          !!!cp (221.5);
3043        }->{$self->{next_char}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3044      !!!cp (1001);          !!!next-input-character;
3045      ## Don't consume          redo A;
3046      ## No error        } else {
3047      return undef;          !!!cp (221.6);
3048    } elsif ($self->{next_char} == 0x0023) { # #          $self->{ct}->{data} .= ']';
3049      !!!next-input-character;          $self->{state} = CDATA_SECTION_STATE;
3050      if ($self->{next_char} == 0x0078 or # x          ## Reconsume.
3051          $self->{next_char} == 0x0058) { # X          redo A;
3052        my $code;        }
3053        X: {      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          my $x_char = $self->{next_char};        if ($self->{nc} == 0x003E) { # >
3055          !!!next-input-character;          $self->{state} = DATA_STATE;
3056          if (0x0030 <= $self->{next_char} and          !!!next-input-character;
3057              $self->{next_char} <= 0x0039) { # 0..9          if (length $self->{ct}->{data}) { # character
3058            !!!cp (1002);            !!!cp (221.7);
3059            $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;  
3060          } else {          } else {
3061            !!!cp (1007);            !!!cp (221.8);
3062            !!!parse-error (type => 'no refc');            ## No token to emit. $self->{ct} is discarded.
3063          }          }
3064            redo A;
3065          } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!cp (221.9); # character
3067            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068            ## Stay in the state.
3069            !!!next-input-character;
3070            redo A;
3071          } else {
3072            !!!cp (221.11);
3073            $self->{ct}->{data} .= ']]'; # character
3074            $self->{state} = CDATA_SECTION_STATE;
3075            ## Reconsume.
3076            redo A;
3077          }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          if ($is_space->{$self->{nc}} or
3080              {
3081                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082                $self->{entity_add} => 1,
3083              }->{$self->{nc}}) {
3084            !!!cp (1001);
3085            ## Don't consume
3086            ## No error
3087            ## Return nothing.
3088            #
3089          } elsif ($self->{nc} == 0x0023) { # #
3090            !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105            !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112          }
3113    
3114          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        ## NOTE: No character is consumed by the "consume a character
3115            !!!cp (1008);        ## reference" algorithm.  In other word, there is an "&" character
3116            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        ## that does not introduce a character reference, which would be
3117            $code = 0xFFFD;        ## appended to the parent element or the attribute value in later
3118          } elsif ($code > 0x10FFFF) {        ## process of the tokenizer.
3119            !!!cp (1009);  
3120            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);        if ($self->{prev_state} == DATA_STATE) {
3121            $code = 0xFFFD;          !!!cp (997);
3122          } elsif ($code == 0x000D) {          $self->{state} = $self->{prev_state};
3123            !!!cp (1010);          ## Reconsume.
3124            !!!parse-error (type => 'CR character reference');          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125            $code = 0x000A;                    line => $self->{line_prev},
3126          } elsif (0x80 <= $code and $code <= 0x9F) {                    column => $self->{column_prev},
3127            !!!cp (1011);                   });
3128            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          redo A;
3129            $code = $c1_entity_char->{$code};        } else {
3130          }          !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132          return {type => CHARACTER_TOKEN, data => chr $code,          $self->{state} = $self->{prev_state};
3133                  has_reference => 1};          ## Reconsume.
3134        } # X          redo A;
3135      } elsif (0x0030 <= $self->{next_char} and        }
3136               $self->{next_char} <= 0x0039) { # 0..9      } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137        my $code = $self->{next_char} - 0x0030;        if ($self->{nc} == 0x0078 or # x
3138        !!!next-input-character;            $self->{nc} == 0x0058) { # X
3139                  !!!cp (995);
3140        while (0x0030 <= $self->{next_char} and          $self->{state} = HEXREF_X_STATE;
3141                  $self->{next_char} <= 0x0039) { # 0..9          $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156            ## NOTE: According to the spec algorithm, nothing is returned,
3157            ## and then "&#" is appended to the parent element or the attribute
3158            ## value in the later processing.
3159    
3160            if ($self->{prev_state} == DATA_STATE) {
3161              !!!cp (1019);
3162              $self->{state} = $self->{prev_state};
3163              ## Reconsume.
3164              !!!emit ({type => CHARACTER_TOKEN,
3165                        data => '&#',
3166                        line => $self->{line_prev},
3167                        column => $self->{column_prev} - 1,
3168                       });
3169              redo A;
3170            } else {
3171              !!!cp (993);
3172              $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176            }
3177          }
3178        } elsif ($self->{state} == NCR_NUM_STATE) {
3179          if (0x0030 <= $self->{nc} and
3180              $self->{nc} <= 0x0039) { # 0..9
3181          !!!cp (1012);          !!!cp (1012);
3182          $code *= 10;          $self->{s_kwd} *= 10;
3183          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3184                    
3185            ## Stay in the state.
3186          !!!next-input-character;          !!!next-input-character;
3187        }          redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3189          !!!cp (1013);          !!!cp (1013);
3190          !!!next-input-character;          !!!next-input-character;
3191            #
3192        } else {        } else {
3193          !!!cp (1014);          !!!cp (1014);
3194          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203          !!!cp (1015);          !!!cp (1015);
3204          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!parse-error (type => 'invalid character reference',
3205          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3209          !!!cp (1016);          !!!cp (1016);
3210          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213          $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};  
3214        }        }
3215          
3216        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3217      } else {          !!!cp (992);
3218        !!!cp (1019);          $self->{state} = $self->{prev_state};
3219        !!!parse-error (type => 'bare nero');          ## Reconsume.
3220        !!!back-next-input-character ($self->{next_char});          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221        $self->{next_char} = 0x0023; # #                    line => $l, column => $c,
3222        return undef;                   });
3223      }          redo A;
3224    } elsif ((0x0041 <= $self->{next_char} and        } else {
3225              $self->{next_char} <= 0x005A) or          !!!cp (991);
3226             (0x0061 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3227              $self->{next_char} <= 0x007A)) {          $self->{ca}->{has_reference} = 1;
3228      my $entity_name = chr $self->{next_char};          $self->{state} = $self->{prev_state};
3229      !!!next-input-character;          ## Reconsume.
3230            redo A;
3231      my $value = $entity_name;        }
3232      my $match = 0;      } elsif ($self->{state} == HEXREF_X_STATE) {
3233      require Whatpm::_NamedEntityList;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234      our $EntityChar;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236      while (length $entity_name < 10 and          # 0..9, A..F, a..f
3237             ## NOTE: Some number greater than the maximum length of entity name          !!!cp (990);
3238             ((0x0041 <= $self->{next_char} and # a          $self->{state} = HEXREF_HEX_STATE;
3239               $self->{next_char} <= 0x005A) or # x          $self->{s_kwd} = 0;
3240              (0x0061 <= $self->{next_char} and # a          ## Reconsume.
3241               $self->{next_char} <= 0x007A) or # z          redo A;
3242              (0x0030 <= $self->{next_char} and # 0        } else {
3243               $self->{next_char} <= 0x0039) or # 9          !!!parse-error (type => 'bare hcro',
3244              $self->{next_char} == 0x003B)) { # ;                          line => $self->{line_prev},
3245        $entity_name .= chr $self->{next_char};                          column => $self->{column_prev} - 2);
3246        if (defined $EntityChar->{$entity_name}) {  
3247          if ($self->{next_char} == 0x003B) { # ;          ## NOTE: According to the spec algorithm, nothing is returned,
3248            !!!cp (1020);          ## and then "&#" followed by "X" or "x" is appended to the parent
3249            $value = $EntityChar->{$entity_name};          ## element or the attribute value in the later processing.
3250            $match = 1;  
3251            !!!next-input-character;          if ($self->{prev_state} == DATA_STATE) {
3252            last;            !!!cp (1005);
3253              $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261          } else {          } else {
3262            !!!cp (1021);            !!!cp (989);
3263            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264            $match = -1;            $self->{state} = $self->{prev_state};
3265            !!!next-input-character;            ## Reconsume.
3266              redo A;
3267          }          }
3268        } else {        }
3269          !!!cp (1022);      } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          $value .= chr $self->{next_char};        if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271          $match *= 2;          # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292          !!!next-input-character;          !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305        }        }
3306      }  
3307              my $code = $self->{s_kwd};
3308      if ($match > 0) {        my $l = $self->{line_prev};
3309        !!!cp (1023);        my $c = $self->{column_prev};
3310        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($charref_map->{$code}) {
3311      } elsif ($match < 0) {          !!!cp (1008);
3312        !!!parse-error (type => 'no refc');          !!!parse-error (type => 'invalid character reference',
3313        if ($in_attr and $match < -1) {                          text => (sprintf 'U+%04X', $code),
3314          !!!cp (1024);                          line => $l, column => $c);
3315          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          $code = $charref_map->{$code};
3316          } elsif ($code > 0x10FFFF) {
3317            !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321            $code = 0xFFFD;
3322          }
3323    
3324          if ($self->{prev_state} == DATA_STATE) {
3325            !!!cp (988);
3326            $self->{state} = $self->{prev_state};
3327            ## Reconsume.
3328            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329                      line => $l, column => $c,
3330                     });
3331            redo A;
3332          } else {
3333            !!!cp (987);
3334            $self->{ca}->{value} .= chr $code;
3335            $self->{ca}->{has_reference} = 1;
3336            $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338            redo A;
3339          }
3340        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341          if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343              ((0x0041 <= $self->{nc} and # a
3344                $self->{nc} <= 0x005A) or # x
3345               (0x0061 <= $self->{nc} and # a
3346                $self->{nc} <= 0x007A) or # z
3347               (0x0030 <= $self->{nc} and # 0
3348                $self->{nc} <= 0x0039) or # 9
3349               $self->{nc} == 0x003B)) { # ;
3350            our $EntityChar;
3351            $self->{s_kwd} .= chr $self->{nc};
3352            if (defined $EntityChar->{$self->{s_kwd}}) {
3353              if ($self->{nc} == 0x003B) { # ;
3354                !!!cp (1020);
3355                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356                $self->{entity__match} = 1;
3357                !!!next-input-character;
3358                #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367            } else {
3368              !!!cp (1022);
3369              $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372              !!!next-input-character;
3373              redo A;
3374            }
3375          }
3376    
3377          my $data;
3378          my $has_ref;
3379          if ($self->{entity__match} > 0) {
3380            !!!cp (1023);
3381            $data = $self->{entity__value};
3382            $has_ref = 1;
3383            #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396            }
3397        } else {        } else {
3398          !!!cp (1025);          !!!cp (1026);
3399          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!parse-error (type => 'bare ero',
3400                            line => $self->{line_prev},
3401                            column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404          }
3405      
3406          ## NOTE: In these cases, when a character reference is found,
3407          ## it is consumed and a character token is returned, or, otherwise,
3408          ## nothing is consumed and returned, according to the spec algorithm.
3409          ## In this implementation, anything that has been examined by the
3410          ## tokenizer is appended to the parent element or the attribute value
3411          ## as string, either literal string when no character reference or
3412          ## entity-replaced string otherwise, in this stage, since any characters
3413          ## that would not be consumed are appended in the data state or in an
3414          ## appropriate attribute value state anyway.
3415    
3416          if ($self->{prev_state} == DATA_STATE) {
3417            !!!cp (986);
3418            $self->{state} = $self->{prev_state};
3419            ## Reconsume.
3420            !!!emit ({type => CHARACTER_TOKEN,
3421                      data => $data,
3422                      line => $self->{line_prev},
3423                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
3434      } else {      } else {
3435        !!!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};  
3436      }      }
3437    } else {    } # A  
3438      !!!cp (1027);  
3439      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3440      !!!parse-error (type => 'bare ero');  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 2400  sub _initialize_tree_constructor ($) { Line 3446  sub _initialize_tree_constructor ($) {
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2428  sub _construct_tree ($) { Line 3476  sub _construct_tree ($) {
3476    
3477    undef $self->{form_element};    undef $self->{form_element};
3478    undef $self->{head_element};    undef $self->{head_element};
3479      undef $self->{head_element_inserted};
3480    $self->{open_elements} = [];    $self->{open_elements} = [];
3481    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3482    
# Line 2454  sub _tree_construction_initial ($) { Line 3503  sub _tree_construction_initial ($) {
3503        ## language.        ## language.
3504        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3505        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3506        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3507        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3508            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3509          !!!cp ('t1');          !!!cp ('t1');
3510          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3511        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3512          !!!cp ('t2');          !!!cp ('t2');
3513          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!parse-error (type => 'not HTML5', token => $token);
3514          !!!parse-error (type => 'not HTML5');        } elsif (defined $token->{pubid}) {
3515            if ($token->{pubid} eq 'XSLT-compat') {
3516              !!!cp ('t1.2');
3517              !!!parse-error (type => 'XSLT-compat', token => $token,
3518                              level => $self->{level}->{should});
3519            } else {
3520              !!!parse-error (type => 'not HTML5', token => $token);
3521            }
3522        } else {        } else {
3523          !!!cp ('t3');          !!!cp ('t3');
3524            #
3525        }        }
3526                
3527        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3528          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3529        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3530            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3531        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3532            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3533        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3534        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3535        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 2481  sub _tree_construction_initial ($) { Line 3537  sub _tree_construction_initial ($) {
3537        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3538          !!!cp ('t4');          !!!cp ('t4');
3539          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3540        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3541          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3542          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3543          if ({          my $prefix = [
3544            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3545            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3548            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3550            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3551            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3552            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3553            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3554            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3555            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3556            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3557            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3558            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3559            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3560            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3561            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3562            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3563            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3564            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3565            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3566            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3568            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3570            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3572            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3574            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3575            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3577            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3578            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3579            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3580            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3581            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3587            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3588            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3589            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3591            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3592            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3593            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3594            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3595            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3596            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3597            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3598            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3599            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3600            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3601            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3602            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3603            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3604            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3605            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3606            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3607            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3608            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3609            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3610            "-//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}) {  
3611            !!!cp ('t5');            !!!cp ('t5');
3612            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3613          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3614                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3615            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3616              !!!cp ('t6');              !!!cp ('t6');
3617              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3618            } else {            } else {
3619              !!!cp ('t7');              !!!cp ('t7');
3620              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3621            }            }
3622          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3623                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3624            !!!cp ('t8');            !!!cp ('t8');
3625            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3626          } else {          } else {
# Line 2579  sub _tree_construction_initial ($) { Line 3629  sub _tree_construction_initial ($) {
3629        } else {        } else {
3630          !!!cp ('t10');          !!!cp ('t10');
3631        }        }
3632        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3633          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3634          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3635          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3636            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3637              ## marked as quirks.
3638            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3639            !!!cp ('t11');            !!!cp ('t11');
3640          } else {          } else {
# Line 2602  sub _tree_construction_initial ($) { Line 3653  sub _tree_construction_initial ($) {
3653                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3654               }->{$token->{type}}) {               }->{$token->{type}}) {
3655        !!!cp ('t14');        !!!cp ('t14');
3656        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3657        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3658        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3659        ## reprocess        ## reprocess
3660          !!!ack-later;
3661        return;        return;
3662      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3663        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3664          ## Ignore the token          ## Ignore the token
3665    
3666          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 2623  sub _tree_construction_initial ($) { Line 3675  sub _tree_construction_initial ($) {
3675          !!!cp ('t17');          !!!cp ('t17');
3676        }        }
3677    
3678        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3679        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3680        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3681        ## reprocess        ## reprocess
# Line 2652  sub _tree_construction_root_element ($) Line 3704  sub _tree_construction_root_element ($)
3704    B: {    B: {
3705        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3706          !!!cp ('t19');          !!!cp ('t19');
3707          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3708          ## Ignore the token          ## Ignore the token
3709          ## Stay in the insertion mode.          ## Stay in the insertion mode.
3710          !!!next-token;          !!!next-token;
# Line 2665  sub _tree_construction_root_element ($) Line 3717  sub _tree_construction_root_element ($)
3717          !!!next-token;          !!!next-token;
3718          redo B;          redo B;
3719        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3720          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3721            ## Ignore the token.            ## Ignore the token.
3722    
3723            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 2686  sub _tree_construction_root_element ($) Line 3738  sub _tree_construction_root_element ($)
3738        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3739          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3740            my $root_element;            my $root_element;
3741            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes});            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3742            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3743            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3744                  [$root_element, $el_category->{html}];
3745    
3746            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3747              !!!cp ('t24');              !!!cp ('t24');
3748              $self->{application_cache_selection}              $self->{application_cache_selection}
3749                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3750              ## ISSUE: No relative reference resolution?              ## ISSUE: Spec is unclear on relative references.
3751                ## According to Hixie (#whatwg 2008-03-19), it should be
3752                ## resolved against the base URI of the document in HTML
3753                ## or xml:base of the element in XHTML.
3754            } else {            } else {
3755              !!!cp ('t25');              !!!cp ('t25');
3756              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3757            }            }
3758    
3759              !!!nack ('t25c');
3760    
3761            !!!next-token;            !!!next-token;
3762            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3763          } else {          } else {
# Line 2716  sub _tree_construction_root_element ($) Line 3774  sub _tree_construction_root_element ($)
3774          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3775        }        }
3776    
3777      my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3778        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3779      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3780      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3781    
3782      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3783    
3784      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3785        !!!ack-later;
3786      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3787    } # B    } # B
3788    
3789    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 2746  sub _reset_insertion_mode ($) { Line 3804  sub _reset_insertion_mode ($) {
3804        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3805          $last = 1;          $last = 1;
3806          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3807            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3808                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3809              !!!cp ('t27');          } else {
3810              #            die "_reset_insertion_mode: t27";
           } else {  
             !!!cp ('t28');  
             $node = $self->{inner_html_node};  
           }  
3811          }          }
3812        }        }
3813              
3814        ## Step 4..13        ## Step 4..14
3815        my $new_mode = {        my $new_mode;
3816          if ($node->[1] & FOREIGN_EL) {
3817            !!!cp ('t28.1');
3818            ## NOTE: Strictly spaking, the line below only applies to MathML and
3819            ## SVG elements.  Currently the HTML syntax supports only MathML and
3820            ## SVG elements as foreigners.
3821            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3822          } elsif ($node->[1] & TABLE_CELL_EL) {
3823            if ($last) {
3824              !!!cp ('t28.2');
3825              #
3826            } else {
3827              !!!cp ('t28.3');
3828              $new_mode = IN_CELL_IM;
3829            }
3830          } else {
3831            !!!cp ('t28.4');
3832            $new_mode = {
3833                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3834                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3835                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
3836                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3837                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3838                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2774  sub _reset_insertion_mode ($) { Line 3843  sub _reset_insertion_mode ($) {
3843                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3844                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3845                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3846                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3847          }
3848        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3849                
3850        ## Step 14        ## Step 15
3851        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3852          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3853            !!!cp ('t29');            !!!cp ('t29');
3854            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2792  sub _reset_insertion_mode ($) { Line 3862  sub _reset_insertion_mode ($) {
3862          !!!cp ('t31');          !!!cp ('t31');
3863        }        }
3864                
3865        ## Step 15        ## Step 16
3866        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3867                
3868        ## Step 16        ## Step 17
3869        $i--;        $i--;
3870        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3871                
3872        ## Step 17        ## Step 18
3873        redo S3;        redo S3;
3874      } # S3      } # S3
3875    
# Line 2911  sub _tree_construction_main ($) { Line 3981  sub _tree_construction_main ($) {
3981      ## Step 1      ## Step 1
3982      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3983      my $el;      my $el;
3984      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3985    
3986      ## Step 2      ## Step 2
3987      $insert->($el);      $insert->($el);
# Line 2922  sub _tree_construction_main ($) { Line 3992  sub _tree_construction_main ($) {
3992    
3993      ## Step 4      ## Step 4
3994      my $text = '';      my $text = '';
3995        !!!nack ('t40.1');
3996      !!!next-token;      !!!next-token;
3997      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3998        !!!cp ('t40');        !!!cp ('t40');
# Line 2948  sub _tree_construction_main ($) { Line 4019  sub _tree_construction_main ($) {
4019        ## NOTE: An end-of-file token.        ## NOTE: An end-of-file token.
4020        if ($content_model_flag == CDATA_CONTENT_MODEL) {        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4021          !!!cp ('t43');          !!!cp ('t43');
4022          !!!parse-error (type => 'in CDATA:#'.$token->{type});          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4023        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4024          !!!cp ('t44');          !!!cp ('t44');
4025          !!!parse-error (type => 'in RCDATA:#'.$token->{type});          !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4026        } else {        } else {
4027          die "$0: $content_model_flag in parse_rcdata";          die "$0: $content_model_flag in parse_rcdata";
4028        }        }
# Line 2961  sub _tree_construction_main ($) { Line 4032  sub _tree_construction_main ($) {
4032    
4033    my $script_start_tag = sub () {    my $script_start_tag = sub () {
4034      my $script_el;      my $script_el;
4035      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4036      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4037    
4038      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4039      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4040            
4041      my $text = '';      my $text = '';
4042        !!!nack ('t45.1');
4043      !!!next-token;      !!!next-token;
4044      while ($token->{type} == CHARACTER_TOKEN) {      while ($token->{type} == CHARACTER_TOKEN) {
4045        !!!cp ('t45');        !!!cp ('t45');
# Line 2987  sub _tree_construction_main ($) { Line 4059  sub _tree_construction_main ($) {
4059        ## Ignore the token        ## Ignore the token
4060      } else {      } else {
4061        !!!cp ('t48');        !!!cp ('t48');
4062        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!parse-error (type => 'in CDATA:#eof', token => $token);
4063        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4064        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4065      }      }
# Line 3010  sub _tree_construction_main ($) { Line 4082  sub _tree_construction_main ($) {
4082      !!!next-token;      !!!next-token;
4083    }; # $script_start_tag    }; # $script_start_tag
4084    
4085      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4086      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4087      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4088      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4089    
4090    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4091      my $tag_name = shift;      my $end_tag_token = shift;
4092        my $tag_name = $end_tag_token->{tag_name};
4093    
4094        ## NOTE: The adoption agency algorithm (AAA).
4095    
4096      FET: {      FET: {
4097        ## Step 1        ## Step 1
4098        my $formatting_element;        my $formatting_element;
4099        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4100        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4101          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4102              !!!cp ('t52');
4103              last AFE;
4104            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4105                         eq $tag_name) {
4106            !!!cp ('t51');            !!!cp ('t51');
4107            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4108            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4109            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
4110          }          }
4111        } # AFE        } # AFE
4112        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4113          !!!cp ('t53');          !!!cp ('t53');
4114          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4115          ## Ignore the token          ## Ignore the token
4116          !!!next-token;          !!!next-token;
4117          return;          return;
# Line 3047  sub _tree_construction_main ($) { Line 4128  sub _tree_construction_main ($) {
4128              last INSCOPE;              last INSCOPE;
4129            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4130              !!!cp ('t55');              !!!cp ('t55');
4131              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
4132                                text => $token->{tag_name},
4133                                token => $end_tag_token);
4134              ## Ignore the token              ## Ignore the token
4135              !!!next-token;              !!!next-token;
4136              return;              return;
4137            }            }
4138          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4139            !!!cp ('t56');            !!!cp ('t56');
4140            $in_scope = 0;            $in_scope = 0;
4141          }          }
4142        } # INSCOPE        } # INSCOPE
4143        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4144          !!!cp ('t57');          !!!cp ('t57');
4145          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!parse-error (type => 'unmatched end tag',
4146                            text => $token->{tag_name},
4147                            token => $end_tag_token);
4148          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4149          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4150          return;          return;
4151        }        }
4152        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4153          !!!cp ('t58');          !!!cp ('t58');
4154          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!parse-error (type => 'not closed',
4155                            text => $self->{open_elements}->[-1]->[0]
4156                                ->manakai_local_name,
4157                            token => $end_tag_token);
4158        }        }
4159                
4160        ## Step 2        ## Step 2
# Line 3077  sub _tree_construction_main ($) { Line 4162  sub _tree_construction_main ($) {
4162        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4163        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4164          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4165          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4166              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4167              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4168               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4169            !!!cp ('t59');            !!!cp ('t59');
4170            $furthest_block = $node;            $furthest_block = $node;
4171            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
# Line 3166  sub _tree_construction_main ($) { Line 4251  sub _tree_construction_main ($) {
4251        } # S7          } # S7  
4252                
4253        ## Step 8        ## Step 8
4254        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4255            my $foster_parent_element;
4256            my $next_sibling;
4257            OE: for (reverse 0..$#{$self->{open_elements}}) {
4258              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4259                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4260                                 if (defined $parent and $parent->node_type == 1) {
4261                                   !!!cp ('t65.1');
4262                                   $foster_parent_element = $parent;
4263                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4264                                 } else {
4265                                   !!!cp ('t65.2');
4266                                   $foster_parent_element
4267                                     = $self->{open_elements}->[$_ - 1]->[0];
4268                                 }
4269                                 last OE;
4270                               }
4271                             } # OE
4272                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4273                               unless defined $foster_parent_element;
4274            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4275            $open_tables->[-1]->[1] = 1; # tainted
4276          } else {
4277            !!!cp ('t65.3');
4278            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4279          }
4280                
4281        ## Step 9        ## Step 9
4282        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 3212  sub _tree_construction_main ($) { Line 4322  sub _tree_construction_main ($) {
4322      } # FET      } # FET
4323    }; # $formatting_end_tag    }; # $formatting_end_tag
4324    
   ## NOTE: $open_tables->[-1]->[0] is the "current table".  
   ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.  
   my $open_tables = [[$self->{open_elements}->[0]->[0]]];  
   
4325    $insert = my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4326      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4327    }; # $insert_to_current    }; # $insert_to_current
4328    
4329    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4330      my $child = shift;      my $child = shift;
4331      if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
          table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
         }->{$self->{open_elements}->[-1]->[1]}) {  
4332        # MUST        # MUST
4333        my $foster_parent_element;        my $foster_parent_element;
4334        my $next_sibling;        my $next_sibling;
4335                           OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4336                             if ($self->{open_elements}->[$_]->[1] eq 'table') {          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4337                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4338                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4339                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3254  sub _tree_construction_main ($) { Line 4358  sub _tree_construction_main ($) {
4358      }      }
4359    }; # $insert_to_foster    }; # $insert_to_foster
4360    
4361    B: {    ## NOTE: When a character is inserted, if the last node that was
4362      ## inserted by the parser is a Text node and the character has to be
4363      ## inserted after that node, then the character is appended to the
4364      ## Text node.  However, if any other node is inserted by the parser,
4365      ## then a new Text node is created and the character is appended as
4366      ## that Text node.  If I'm not wrong, there are only two cases where
4367      ## this occurs.  One is the case where an element node is inserted
4368      ## to the |head| element.  This is covered by using the
4369      ## |$self->{head_element_inserted}| flag.  Another is the case where
4370      ## an element or comment is inserted into the |table| subtree while
4371      ## foster parenting happens.  This is covered by using the [2] flag
4372      ## of the |$open_tables| structure.  All other cases are handled
4373      ## simply by calling |manakai_append_text| method.
4374    
4375      B: while (1) {
4376      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4377        !!!cp ('t73');        !!!cp ('t73');
4378        !!!parse-error (type => 'DOCTYPE in the middle');        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4379        ## Ignore the token        ## Ignore the token
4380        ## Stay in the phase        ## Stay in the phase
4381        !!!next-token;        !!!next-token;
4382        redo B;        next B;
     } elsif ($token->{type} == END_OF_FILE_TOKEN) {  
       if ($self->{insertion_mode} & AFTER_HTML_IMS) {  
         !!!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;  
4383      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4384               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4385        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4386          !!!cp ('t79');          !!!cp ('t79');
4387          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4388          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4389        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4390          !!!cp ('t80');          !!!cp ('t80');
4391          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4392          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4393        } else {        } else {
4394          !!!cp ('t81');          !!!cp ('t81');
4395        }        }
4396    
4397        !!!cp ('t82');        !!!cp ('t82');
4398        !!!parse-error (type => 'not first start tag');        !!!parse-error (type => 'not first start tag', token => $token);
4399        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4400        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4401          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
# Line 3319  sub _tree_construction_main ($) { Line 4405  sub _tree_construction_main ($) {
4405               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4406          }          }
4407        }        }
4408          !!!nack ('t84.1');
4409        !!!next-token;        !!!next-token;
4410        redo B;        next B;
4411      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4412        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4413        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3332  sub _tree_construction_main ($) { Line 4419  sub _tree_construction_main ($) {
4419        } else {        } else {
4420          !!!cp ('t87');          !!!cp ('t87');
4421          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4422            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4423        }        }
4424        !!!next-token;        !!!next-token;
4425        redo B;        next B;
4426      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4427        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4428          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t87.1');
4429            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4430            !!!next-token;
4431            next B;
4432          } elsif ($token->{type} == START_TAG_TOKEN) {
4433            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4434                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4435                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4436                ($token->{tag_name} eq 'svg' and
4437                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4438              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4439              !!!cp ('t87.2');
4440              #
4441            } elsif ({
4442                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4443                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4444                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4445                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4446                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4447                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4448                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4449                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4450                     }->{$token->{tag_name}}) {
4451              !!!cp ('t87.2');
4452              !!!parse-error (type => 'not closed',
4453                              text => $self->{open_elements}->[-1]->[0]
4454                                  ->manakai_local_name,
4455                              token => $token);
4456    
4457              pop @{$self->{open_elements}}
4458                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4459    
4460              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4461              ## Reprocess.
4462              next B;
4463            } else {
4464              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4465              my $tag_name = $token->{tag_name};
4466              if ($nsuri eq $SVG_NS) {
4467                $tag_name = {
4468                   altglyph => 'altGlyph',
4469                   altglyphdef => 'altGlyphDef',
4470                   altglyphitem => 'altGlyphItem',
4471                   animatecolor => 'animateColor',
4472                   animatemotion => 'animateMotion',
4473                   animatetransform => 'animateTransform',
4474                   clippath => 'clipPath',
4475                   feblend => 'feBlend',
4476                   fecolormatrix => 'feColorMatrix',
4477                   fecomponenttransfer => 'feComponentTransfer',
4478                   fecomposite => 'feComposite',
4479                   feconvolvematrix => 'feConvolveMatrix',
4480                   fediffuselighting => 'feDiffuseLighting',
4481                   fedisplacementmap => 'feDisplacementMap',
4482                   fedistantlight => 'feDistantLight',
4483                   feflood => 'feFlood',
4484                   fefunca => 'feFuncA',
4485                   fefuncb => 'feFuncB',
4486                   fefuncg => 'feFuncG',
4487                   fefuncr => 'feFuncR',
4488                   fegaussianblur => 'feGaussianBlur',
4489                   feimage => 'feImage',
4490                   femerge => 'feMerge',
4491                   femergenode => 'feMergeNode',
4492                   femorphology => 'feMorphology',
4493                   feoffset => 'feOffset',
4494                   fepointlight => 'fePointLight',
4495                   fespecularlighting => 'feSpecularLighting',
4496                   fespotlight => 'feSpotLight',
4497                   fetile => 'feTile',
4498                   feturbulence => 'feTurbulence',
4499                   foreignobject => 'foreignObject',
4500                   glyphref => 'glyphRef',
4501                   lineargradient => 'linearGradient',
4502                   radialgradient => 'radialGradient',
4503                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4504                   textpath => 'textPath',  
4505                }->{$tag_name} || $tag_name;
4506              }
4507    
4508              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4509    
4510              ## "adjust foreign attributes" - done in insert-element-f
4511    
4512              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4513    
4514              if ($self->{self_closing}) {
4515                pop @{$self->{open_elements}};
4516                !!!ack ('t87.3');
4517              } else {
4518                !!!cp ('t87.4');
4519              }
4520    
4521              !!!next-token;
4522              next B;
4523            }
4524          } elsif ($token->{type} == END_TAG_TOKEN) {
4525            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4526            !!!cp ('t87.5');
4527            #
4528          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4529            !!!cp ('t87.6');
4530            !!!parse-error (type => 'not closed',
4531                            text => $self->{open_elements}->[-1]->[0]
4532                                ->manakai_local_name,
4533                            token => $token);
4534    
4535            pop @{$self->{open_elements}}
4536                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4537    
4538            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4539    
4540            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4541            ## Reprocess.
4542            next B;
4543          } else {
4544            die "$0: $token->{type}: Unknown token type";        
4545          }
4546        }
4547    
4548        if ($self->{insertion_mode} & HEAD_IMS) {
4549          if ($token->{type} == CHARACTER_TOKEN) {
4550            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4551            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
4553              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
4554                  $self->{open_elements}->[-1]->[0]->append_child
4555                    ($self->{document}->create_text_node ($1));
4556                  delete $self->{head_element_inserted};
4557                  ## NOTE: |</head> <link> |
4558                  #
4559                } else {
4560                  !!!cp ('t88.2');
4561                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4562                  ## NOTE: |</head> &#x20;|
4563                  #
4564                }
4565            } else {            } else {
4566              !!!cp ('t88.1');              !!!cp ('t88.1');
4567              ## Ignore the token.              ## Ignore the token.
4568              !!!next-token;              #
             redo B;  
4569            }            }
4570            unless (length $token->{data}) {            unless (length $token->{data}) {
4571              !!!cp ('t88');              !!!cp ('t88');
4572              !!!next-token;              !!!next-token;
4573              redo B;              next B;
4574            }            }
4575    ## TODO: set $token->{column} appropriately
4576          }          }
4577    
4578          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4579            !!!cp ('t89');            !!!cp ('t89');
4580            ## As if <head>            ## As if <head>
4581            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4582            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4583            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4584                  [$self->{head_element}, $el_category->{head}];
4585    
4586            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4587            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3369  sub _tree_construction_main ($) { Line 4591  sub _tree_construction_main ($) {
4591            !!!cp ('t90');            !!!cp ('t90');
4592            ## As if </noscript>            ## As if </noscript>
4593            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4594            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4595                        
4596            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4597            ## As if </head>            ## As if </head>
# Line 3385  sub _tree_construction_main ($) { Line 4607  sub _tree_construction_main ($) {
4607            !!!cp ('t92');            !!!cp ('t92');
4608          }          }
4609    
4610              ## "after head" insertion mode          ## "after head" insertion mode
4611              ## As if <body>          ## As if <body>
4612              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4613              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4614              ## reprocess          ## reprocess
4615              redo B;          next B;
4616            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4617              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4618                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4619                  !!!cp ('t93');              !!!cp ('t93');
4620                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4621                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4622                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4623                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4624                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4625                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4626                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4627                  !!!cp ('t94');              !!!next-token;
4628                  #              next B;
4629                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4630                  !!!cp ('t95');              !!!cp ('t93.2');
4631                  !!!parse-error (type => 'in head:head'); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4632                  ## Ignore the token                              token => $token);
4633                  !!!next-token;              ## Ignore the token
4634                  redo B;              !!!nack ('t93.3');
4635                }              !!!next-token;
4636              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4637                !!!cp ('t96');            } else {
4638                ## As if <head>              !!!cp ('t95');
4639                !!!create-element ($self->{head_element}, 'head');              !!!parse-error (type => 'in head:head',
4640                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4641                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4642                !!!nack ('t95.1');
4643                !!!next-token;
4644                next B;
4645              }
4646            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4647              !!!cp ('t96');
4648              ## As if <head>
4649              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4650              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4651              push @{$self->{open_elements}},
4652                  [$self->{head_element}, $el_category->{head}];
4653    
4654                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4655                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4656              } else {          } else {
4657                !!!cp ('t97');            !!!cp ('t97');
4658              }          }
4659    
4660              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4661                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4662                  !!!cp ('t98');              !!!cp ('t98');
4663                  ## As if </noscript>              ## As if </noscript>
4664                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4665                  !!!parse-error (type => 'in noscript:base');              !!!parse-error (type => 'in noscript', text => 'base',
4666                                              token => $token);
4667                  $self->{insertion_mode} = IN_HEAD_IM;            
4668                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4669                } else {              ## Reprocess in the "in head" insertion mode...
4670                  !!!cp ('t99');            } else {
4671                }              !!!cp ('t99');
4672              }
4673    
4674                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4675                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4676                  !!!cp ('t100');              !!!cp ('t100');
4677                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4678                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4679                } else {              push @{$self->{open_elements}},
4680                  !!!cp ('t101');                  [$self->{head_element}, $el_category->{head}];
4681                }              $self->{head_element_inserted} = 1;
4682                !!!insert-element ($token->{tag_name}, $token->{attributes});            } else {
4683                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              !!!cp ('t101');
4684                pop @{$self->{open_elements}} # <head>            }
4685                    if $self->{insertion_mode} == AFTER_HEAD_IM;            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4686                !!!next-token;            pop @{$self->{open_elements}};
4687                redo B;            pop @{$self->{open_elements}} # <head>
4688              } elsif ($token->{tag_name} eq 'link') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4689                ## NOTE: There is a "as if in head" code clone.            !!!nack ('t101.1');
4690                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            !!!next-token;
4691                  !!!cp ('t102');            next B;
4692                  !!!parse-error (type => 'after head:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'link') {
4693                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            ## NOTE: There is a "as if in head" code clone.
4694                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4695                  !!!cp ('t103');              !!!cp ('t102');
4696                }              !!!parse-error (type => 'after head',
4697                !!!insert-element ($token->{tag_name}, $token->{attributes});                              text => $token->{tag_name}, token => $token);
4698                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              push @{$self->{open_elements}},
4699                pop @{$self->{open_elements}} # <head>                  [$self->{head_element}, $el_category->{head}];
4700                    if $self->{insertion_mode} == AFTER_HEAD_IM;              $self->{head_element_inserted} = 1;
4701                !!!next-token;            } else {
4702                redo B;              !!!cp ('t103');
4703              } elsif ($token->{tag_name} eq 'meta') {            }
4704                ## NOTE: There is a "as if in head" code clone.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4705                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}};
4706                  !!!cp ('t104');            pop @{$self->{open_elements}} # <head>
4707                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4708                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!ack ('t103.1');
4709                } else {            !!!next-token;
4710                  !!!cp ('t105');            next B;
4711                }          } elsif ($token->{tag_name} eq 'command' or
4712                !!!insert-element ($token->{tag_name}, $token->{attributes});                   $token->{tag_name} eq 'eventsource') {
4713                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            if ($self->{insertion_mode} == IN_HEAD_IM) {
4714                ## NOTE: If the insertion mode at the time of the emission
4715                ## of the token was "before head", $self->{insertion_mode}
4716                ## is already changed to |IN_HEAD_IM|.
4717    
4718                ## NOTE: There is a "as if in head" code clone.
4719                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4720                pop @{$self->{open_elements}};
4721                pop @{$self->{open_elements}} # <head>
4722                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4723                !!!ack ('t103.2');
4724                !!!next-token;
4725                next B;
4726              } else {
4727                ## NOTE: "in head noscript" or "after head" insertion mode
4728                ## - in these cases, these tags are treated as same as
4729                ## normal in-body tags.
4730                !!!cp ('t103.3');
4731                #
4732              }
4733            } elsif ($token->{tag_name} eq 'meta') {
4734              ## NOTE: There is a "as if in head" code clone.
4735              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4736                !!!cp ('t104');
4737                !!!parse-error (type => 'after head',
4738                                text => $token->{tag_name}, token => $token);
4739                push @{$self->{open_elements}},
4740                    [$self->{head_element}, $el_category->{head}];
4741                $self->{head_element_inserted} = 1;
4742              } else {
4743                !!!cp ('t105');
4744              }
4745              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4746              my $meta_el = pop @{$self->{open_elements}};
4747    
4748                unless ($self->{confident}) {                unless ($self->{confident}) {
4749                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4750                    !!!cp ('t106');                    !!!cp ('t106');
4751                      ## NOTE: Whether the encoding is supported or not is handled
4752                      ## in the {change_encoding} callback.
4753                    $self->{change_encoding}                    $self->{change_encoding}
4754                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4755                             $token);
4756                                        
4757                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4758                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4759                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4760                                                 ->{has_reference});                                                 ->{has_reference});
4761                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4762                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4763                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4764                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4765                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4766                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4767                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4768                      !!!cp ('t107');                      !!!cp ('t107');
4769                        ## NOTE: Whether the encoding is supported or not is handled
4770                        ## in the {change_encoding} callback.
4771                      $self->{change_encoding}                      $self->{change_encoding}
4772                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4773                               $token);
4774                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4775                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4776                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
# Line 3525  sub _tree_construction_main ($) { Line 4798  sub _tree_construction_main ($) {
4798    
4799                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4800                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4801                  !!!ack ('t110.1');
4802                !!!next-token;                !!!next-token;
4803                redo B;                next B;
4804              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4805                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4806                  !!!cp ('t111');              !!!cp ('t111');
4807                  ## As if </noscript>              ## As if </noscript>
4808                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4809                  !!!parse-error (type => 'in noscript:title');              !!!parse-error (type => 'in noscript', text => 'title',
4810                                              token => $token);
4811                  $self->{insertion_mode} = IN_HEAD_IM;            
4812                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4813                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4814                  !!!cp ('t112');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4815                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t112');
4816                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4817                } else {                              text => $token->{tag_name}, token => $token);
4818                  !!!cp ('t113');              push @{$self->{open_elements}},
4819                }                  [$self->{head_element}, $el_category->{head}];
4820                $self->{head_element_inserted} = 1;
4821                ## NOTE: There is a "as if in head" code clone.            } else {
4822                my $parent = defined $self->{head_element} ? $self->{head_element}              !!!cp ('t113');
4823                    : $self->{open_elements}->[-1]->[0];            }
4824                $parse_rcdata->(RCDATA_CONTENT_MODEL);  
4825                pop @{$self->{open_elements}} # <head>            ## NOTE: There is a "as if in head" code clone.
4826                    if $self->{insertion_mode} == AFTER_HEAD_IM;            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4827                redo B;            pop @{$self->{open_elements}} # <head>
4828              } elsif ($token->{tag_name} eq 'style') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4829                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            next B;
4830                ## insertion mode IN_HEAD_IM)          } elsif ($token->{tag_name} eq 'style' or
4831                ## NOTE: There is a "as if in head" code clone.                   $token->{tag_name} eq 'noframes') {
4832                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4833                  !!!cp ('t114');            ## insertion mode IN_HEAD_IM)
4834                  !!!parse-error (type => 'after head:'.$token->{tag_name});            ## NOTE: There is a "as if in head" code clone.
4835                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4836                } else {              !!!cp ('t114');
4837                  !!!cp ('t115');              !!!parse-error (type => 'after head',
4838                }                              text => $token->{tag_name}, token => $token);
4839                $parse_rcdata->(CDATA_CONTENT_MODEL);              push @{$self->{open_elements}},
4840                pop @{$self->{open_elements}} # <head>                  [$self->{head_element}, $el_category->{head}];
4841                    if $self->{insertion_mode} == AFTER_HEAD_IM;              $self->{head_element_inserted} = 1;
4842                redo B;            } else {
4843                !!!cp ('t115');
4844              }
4845              $parse_rcdata->(CDATA_CONTENT_MODEL);
4846              pop @{$self->{open_elements}} # <head>
4847                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4848              next B;
4849              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4850                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4851                  !!!cp ('t116');                  !!!cp ('t116');
4852                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4853                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4854                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4855                    !!!nack ('t116.1');
4856                  !!!next-token;                  !!!next-token;
4857                  redo B;                  next B;
4858                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4859                  !!!cp ('t117');                  !!!cp ('t117');
4860                  !!!parse-error (type => 'in noscript:noscript');                  !!!parse-error (type => 'in noscript', text => 'noscript',
4861                                    token => $token);
4862                  ## Ignore the token                  ## Ignore the token
4863                    !!!nack ('t117.1');
4864                  !!!next-token;                  !!!next-token;
4865                  redo B;                  next B;
4866                } else {                } else {
4867                  !!!cp ('t118');                  !!!cp ('t118');
4868                  #                  #
4869                }                }
4870              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4871                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4872                  !!!cp ('t119');              !!!cp ('t119');
4873                  ## As if </noscript>              ## As if </noscript>
4874                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4875                  !!!parse-error (type => 'in noscript:script');              !!!parse-error (type => 'in noscript', text => 'script',
4876                                              token => $token);
4877                  $self->{insertion_mode} = IN_HEAD_IM;            
4878                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4879                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4880                  !!!cp ('t120');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4881                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t120');
4882                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4883                } else {                              text => $token->{tag_name}, token => $token);
4884                  !!!cp ('t121');              push @{$self->{open_elements}},
4885                }                  [$self->{head_element}, $el_category->{head}];
4886                $self->{head_element_inserted} = 1;
4887                ## NOTE: There is a "as if in head" code clone.            } else {
4888                $script_start_tag->();              !!!cp ('t121');
4889                pop @{$self->{open_elements}} # <head>            }
4890                    if $self->{insertion_mode} == AFTER_HEAD_IM;  
4891                redo B;            ## NOTE: There is a "as if in head" code clone.
4892              } elsif ($token->{tag_name} eq 'body' or            $script_start_tag->();
4893                       $token->{tag_name} eq 'frameset') {            pop @{$self->{open_elements}} # <head>
4894                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4895              next B;
4896            } elsif ($token->{tag_name} eq 'body' or
4897                     $token->{tag_name} eq 'frameset') {
4898                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4899                  !!!cp ('t122');                  !!!cp ('t122');
4900                  ## As if </noscript>                  ## As if </noscript>
4901                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4902                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4903                                    text => $token->{tag_name}, token => $token);
4904                                    
4905                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4906                  ## As if </head>                  ## As if </head>
# Line 3629  sub _tree_construction_main ($) { Line 4917  sub _tree_construction_main ($) {
4917                }                }
4918    
4919                ## "after head" insertion mode                ## "after head" insertion mode
4920                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4921                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4922                  !!!cp ('t126');                  !!!cp ('t126');
4923                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
# Line 3639  sub _tree_construction_main ($) { Line 4927  sub _tree_construction_main ($) {
4927                } else {                } else {
4928                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4929                }                }
4930                  !!!nack ('t127.1');
4931                !!!next-token;                !!!next-token;
4932                redo B;                next B;
4933              } else {              } else {
4934                !!!cp ('t128');                !!!cp ('t128');
4935                #                #
# Line 3650  sub _tree_construction_main ($) { Line 4939  sub _tree_construction_main ($) {
4939                !!!cp ('t129');                !!!cp ('t129');
4940                ## As if </noscript>                ## As if </noscript>
4941                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4942                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4943                                  text => $token->{tag_name}, token => $token);
4944                                
4945                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4946                ## As if </head>                ## As if </head>
# Line 3669  sub _tree_construction_main ($) { Line 4959  sub _tree_construction_main ($) {
4959    
4960              ## "after head" insertion mode              ## "after head" insertion mode
4961              ## As if <body>              ## As if <body>
4962              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4963              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4964              ## reprocess              ## reprocess
4965              redo B;              !!!ack-later;
4966                next B;
4967            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4968              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4969                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4970                  !!!cp ('t132');                  !!!cp ('t132');
4971                  ## As if <head>                  ## As if <head>
4972                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4973                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4974                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4975                        [$self->{head_element}, $el_category->{head}];
4976    
4977                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4978                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4979                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4980                  !!!next-token;                  !!!next-token;
4981                  redo B;                  next B;
4982                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4983                  !!!cp ('t133');                  !!!cp ('t133');
4984                  ## As if </noscript>                  ## As if </noscript>
4985                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4986                  !!!parse-error (type => 'in noscript:/head');                  !!!parse-error (type => 'in noscript:/',
4987                                    text => 'head', token => $token);
4988                                    
4989                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4990                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4991                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4992                  !!!next-token;                  !!!next-token;
4993                  redo B;                  next B;
4994                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4995                  !!!cp ('t134');                  !!!cp ('t134');
4996                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4997                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4998                  !!!next-token;                  !!!next-token;
4999                  redo B;                  next B;
5000                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5001                    !!!cp ('t134.1');
5002                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5003                                    token => $token);
5004                    ## Ignore the token
5005                    !!!next-token;
5006                    next B;
5007                } else {                } else {
5008                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
5009                }                }
5010              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5011                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3714  sub _tree_construction_main ($) { Line 5013  sub _tree_construction_main ($) {
5013                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5014                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5015                  !!!next-token;                  !!!next-token;
5016                  redo B;                  next B;
5017                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5018                           $self->{insertion_mode} == AFTER_HEAD_IM) {
5019                  !!!cp ('t137');                  !!!cp ('t137');
5020                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!parse-error (type => 'unmatched end tag',
5021                                    text => 'noscript', token => $token);
5022                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5023                  !!!next-token;                  !!!next-token;
5024                  redo B;                  next B;
5025                } else {                } else {
5026                  !!!cp ('t138');                  !!!cp ('t138');
5027                  #                  #
# Line 3728  sub _tree_construction_main ($) { Line 5029  sub _tree_construction_main ($) {
5029              } elsif ({              } elsif ({
5030                        body => 1, html => 1,                        body => 1, html => 1,
5031                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5032                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5033                  !!!cp ('t139');                    $self->{insertion_mode} == IN_HEAD_IM or
5034                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 !!!create-element ($self->{head_element}, 'head');  
                 $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) {  
5035                  !!!cp ('t140');                  !!!cp ('t140');
5036                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5037                                    text => $token->{tag_name}, token => $token);
5038                  ## Ignore the token                  ## Ignore the token
5039                  !!!next-token;                  !!!next-token;
5040                  redo B;                  next B;
5041                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5042                    !!!cp ('t140.1');
5043                    !!!parse-error (type => 'unmatched end tag',
5044                                    text => $token->{tag_name}, token => $token);
5045                    ## Ignore the token
5046                    !!!next-token;
5047                    next B;
5048                } else {                } else {
5049                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5050                }                }
5051                              } elsif ($token->{tag_name} eq 'p') {
5052                #                !!!cp ('t142');
5053              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5054                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5055                       }->{$token->{tag_name}}) {                ## Ignore the token
5056                  !!!next-token;
5057                  next B;
5058                } elsif ($token->{tag_name} eq 'br') {
5059                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5060                  !!!cp ('t142');                  !!!cp ('t142.2');
5061                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5062                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5063                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5064                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5065      
5066                    ## Reprocess in the "after head" insertion mode...
5067                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5068                    !!!cp ('t143.2');
5069                    ## As if </head>
5070                    pop @{$self->{open_elements}};
5071                    $self->{insertion_mode} = AFTER_HEAD_IM;
5072      
5073                    ## Reprocess in the "after head" insertion mode...
5074                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5075                    !!!cp ('t143.3');
5076                    ## ISSUE: Two parse errors for <head><noscript></br>
5077                    !!!parse-error (type => 'unmatched end tag',
5078                                    text => 'br', token => $token);
5079                    ## As if </noscript>
5080                    pop @{$self->{open_elements}};
5081                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5082    
5083                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5084                } else {                  ## As if </head>
5085                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5086                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5087    
5088                #                  ## Reprocess in the "after head" insertion mode...
5089              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5090                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5091                  #                  #
5092                } else {                } else {
5093                  !!!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;  
5094                }                }
5095    
5096                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5097                  !!!parse-error (type => 'unmatched end tag',
5098                                  text => 'br', token => $token);
5099                  ## Ignore the token
5100                  !!!next-token;
5101                  next B;
5102                } else {
5103                  !!!cp ('t145');
5104                  !!!parse-error (type => 'unmatched end tag',
5105                                  text => $token->{tag_name}, token => $token);
5106                  ## Ignore the token
5107                  !!!next-token;
5108                  next B;
5109              }              }
5110    
5111              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5112                !!!cp ('t146');                !!!cp ('t146');
5113                ## As if </noscript>                ## As if </noscript>
5114                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5115                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5116                                  text => $token->{tag_name}, token => $token);
5117                                
5118                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5119                ## As if </head>                ## As if </head>
# Line 3798  sub _tree_construction_main ($) { Line 5129  sub _tree_construction_main ($) {
5129              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5130  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5131                !!!cp ('t148');                !!!cp ('t148');
5132                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5133                                  text => $token->{tag_name}, token => $token);
5134                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5135                !!!next-token;                !!!next-token;
5136                redo B;                next B;
5137              } else {              } else {
5138                !!!cp ('t149');                !!!cp ('t149');
5139              }              }
5140    
5141              ## "after head" insertion mode              ## "after head" insertion mode
5142              ## As if <body>              ## As if <body>
5143              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5144              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5145              ## reprocess              ## reprocess
5146              redo B;              next B;
5147            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5148              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5149            }            !!!cp ('t149.1');
5150    
5151            ## ISSUE: An issue in the spec.            ## NOTE: As if <head>
5152              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5153              $self->{open_elements}->[-1]->[0]->append_child
5154                  ($self->{head_element});
5155              #push @{$self->{open_elements}},
5156              #    [$self->{head_element}, $el_category->{head}];
5157              #$self->{insertion_mode} = IN_HEAD_IM;
5158              ## NOTE: Reprocess.
5159    
5160              ## NOTE: As if </head>
5161              #pop @{$self->{open_elements}};
5162              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5163              ## NOTE: Reprocess.
5164              
5165              #
5166            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5167              !!!cp ('t149.2');
5168    
5169              ## NOTE: As if </head>
5170              pop @{$self->{open_elements}};
5171              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5172              ## NOTE: Reprocess.
5173    
5174              #
5175            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5176              !!!cp ('t149.3');
5177    
5178              !!!parse-error (type => 'in noscript:#eof', token => $token);
5179    
5180              ## As if </noscript>
5181              pop @{$self->{open_elements}};
5182              #$self->{insertion_mode} = IN_HEAD_IM;
5183              ## NOTE: Reprocess.
5184    
5185              ## NOTE: As if </head>
5186              pop @{$self->{open_elements}};
5187              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5188              ## NOTE: Reprocess.
5189    
5190              #
5191            } else {
5192              !!!cp ('t149.4');
5193              #
5194            }
5195    
5196            ## NOTE: As if <body>
5197            !!!insert-element ('body',, $token);
5198            $self->{insertion_mode} = IN_BODY_IM;
5199            ## NOTE: Reprocess.
5200            next B;
5201          } else {
5202            die "$0: $token->{type}: Unknown token type";
5203          }
5204      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5205            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5206              !!!cp ('t150');              !!!cp ('t150');
# Line 3826  sub _tree_construction_main ($) { Line 5210  sub _tree_construction_main ($) {
5210              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5211    
5212              !!!next-token;              !!!next-token;
5213              redo B;              next B;
5214            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5215              if ({              if ({
5216                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3834  sub _tree_construction_main ($) { Line 5218  sub _tree_construction_main ($) {
5218                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5219                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5220                  ## have an element in table scope                  ## have an element in table scope
5221                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5222                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5223                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5224                      !!!cp ('t151');                      !!!cp ('t151');
5225                      $tn = $node->[1];  
5226                      last INSCOPE;                      ## Close the cell
5227                    } elsif ({                      !!!back-token; # <x>
5228                              table => 1, html => 1,                      $token = {type => END_TAG_TOKEN,
5229                             }->{$node->[1]}) {                                tag_name => $node->[0]->manakai_local_name,
5230                                  line => $token->{line},
5231                                  column => $token->{column}};
5232                        next B;
5233                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5234                      !!!cp ('t152');                      !!!cp ('t152');
5235                      last INSCOPE;                      ## ISSUE: This case can never be reached, maybe.
5236                        last;
5237                    }                    }
5238                  } # INSCOPE                  }
5239                    unless (defined $tn) {  
5240                      !!!cp ('t153');                  !!!cp ('t153');
5241  ## TODO: This error type is wrong.                  !!!parse-error (type => 'start tag not allowed',
5242                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      text => $token->{tag_name}, token => $token);
5243                      ## Ignore the token                  ## Ignore the token
5244                      !!!next-token;                  !!!nack ('t153.1');
5245                      redo B;                  !!!next-token;
5246                    }                  next B;
                   
                 !!!cp ('t154');  
                 ## Close the cell  
                 !!!back-token; # <?>  
                 $token = {type => END_TAG_TOKEN, tag_name => $tn};  
                 redo B;  
5247                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5248                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5249                                    token => $token);
5250                                    
5251                  ## As if </caption>                  ## NOTE: As if </caption>.
5252                  ## have a table element in table scope                  ## have a table element in table scope
5253                  my $i;                  my $i;
5254                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5255                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5256                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5257                      !!!cp ('t155');                      if ($node->[1] & CAPTION_EL) {
5258                      $i = $_;                        !!!cp ('t155');
5259                      last INSCOPE;                        $i = $_;
5260                    } elsif ({                        last INSCOPE;
5261                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5262                             }->{$node->[1]}) {                        !!!cp ('t156');
5263                      !!!cp ('t156');                        last;
5264                      last INSCOPE;                      }
5265                    }                    }
5266    
5267                      !!!cp ('t157');
5268                      !!!parse-error (type => 'start tag not allowed',
5269                                      text => $token->{tag_name}, token => $token);
5270                      ## Ignore the token
5271                      !!!nack ('t157.1');
5272                      !!!next-token;
5273                      next B;
5274                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t157');  
 ## TODO: this type is wrong.  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5275                                    
5276                  ## generate implied end tags                  ## generate implied end tags
5277                  while ({                  while ($self->{open_elements}->[-1]->[1]
5278                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5279                    !!!cp ('t158');                    !!!cp ('t158');
5280                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5281                  }                  }
5282    
5283                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5284                    !!!cp ('t159');                    !!!cp ('t159');
5285                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5286                                      text => $self->{open_elements}->[-1]->[0]
5287                                          ->manakai_local_name,
5288                                      token => $token);
5289                  } else {                  } else {
5290                    !!!cp ('t160');                    !!!cp ('t160');
5291                  }                  }
# Line 3912  sub _tree_construction_main ($) { Line 5297  sub _tree_construction_main ($) {
5297                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5298                                    
5299                  ## reprocess                  ## reprocess
5300                  redo B;                  !!!ack-later;
5301                    next B;
5302                } else {                } else {
5303                  !!!cp ('t161');                  !!!cp ('t161');
5304                  #                  #
# Line 3928  sub _tree_construction_main ($) { Line 5314  sub _tree_construction_main ($) {
5314                  my $i;                  my $i;
5315                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5316                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5317                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5318                      !!!cp ('t163');                      !!!cp ('t163');
5319                      $i = $_;                      $i = $_;
5320                      last INSCOPE;                      last INSCOPE;
5321                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5322                      !!!cp ('t164');                      !!!cp ('t164');
5323                      last INSCOPE;                      last INSCOPE;
5324                    }                    }
5325                  } # INSCOPE                  } # INSCOPE
5326                    unless (defined $i) {                    unless (defined $i) {
5327                      !!!cp ('t165');                      !!!cp ('t165');
5328                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
5329                                        text => $token->{tag_name},
5330                                        token => $token);
5331                      ## Ignore the token                      ## Ignore the token
5332                      !!!next-token;                      !!!next-token;
5333                      redo B;                      next B;
5334                    }                    }
5335                                    
5336                  ## generate implied end tags                  ## generate implied end tags
5337                  while ({                  while ($self->{open_elements}->[-1]->[1]
5338                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5339                    !!!cp ('t166');                    !!!cp ('t166');
5340                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5341                  }                  }
5342    
5343                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5344                            ne $token->{tag_name}) {
5345                    !!!cp ('t167');                    !!!cp ('t167');
5346                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5347                                      text => $self->{open_elements}->[-1]->[0]
5348                                          ->manakai_local_name,
5349                                      token => $token);
5350                  } else {                  } else {
5351                    !!!cp ('t168');                    !!!cp ('t168');
5352                  }                  }
# Line 3969  sub _tree_construction_main ($) { Line 5358  sub _tree_construction_main ($) {
5358                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5359                                    
5360                  !!!next-token;                  !!!next-token;
5361                  redo B;                  next B;
5362                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5363                  !!!cp ('t169');                  !!!cp ('t169');
5364                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5365                                    text => $token->{tag_name}, token => $token);
5366                  ## Ignore the token                  ## Ignore the token
5367                  !!!next-token;                  !!!next-token;
5368                  redo B;                  next B;
5369                } else {                } else {
5370                  !!!cp ('t170');                  !!!cp ('t170');
5371                  #                  #
# Line 3984  sub _tree_construction_main ($) { Line 5374  sub _tree_construction_main ($) {
5374                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5375                  ## have a table element in table scope                  ## have a table element in table scope
5376                  my $i;                  my $i;
5377                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5378                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5379                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5380                      !!!cp ('t171');                      if ($node->[1] & CAPTION_EL) {
5381                      $i = $_;                        !!!cp ('t171');
5382                      last INSCOPE;                        $i = $_;
5383                    } elsif ({                        last INSCOPE;
5384                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5385                             }->{$node->[1]}) {                        !!!cp ('t172');
5386                      !!!cp ('t172');                        last;
5387                      last INSCOPE;                      }
5388                    }                    }
5389    
5390                      !!!cp ('t173');
5391                      !!!parse-error (type => 'unmatched end tag',
5392                                      text => $token->{tag_name}, token => $token);
5393                      ## Ignore the token
5394                      !!!next-token;
5395                      next B;
5396                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t173');  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5397                                    
5398                  ## generate implied end tags                  ## generate implied end tags
5399                  while ({                  while ($self->{open_elements}->[-1]->[1]
5400                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5401                    !!!cp ('t174');                    !!!cp ('t174');
5402                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5403                  }                  }
5404                                    
5405                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5406                    !!!cp ('t175');                    !!!cp ('t175');
5407                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5408                                      text => $self->{open_elements}->[-1]->[0]
5409                                          ->manakai_local_name,
5410                                      token => $token);
5411                  } else {                  } else {
5412                    !!!cp ('t176');                    !!!cp ('t176');
5413                  }                  }
# Line 4027  sub _tree_construction_main ($) { Line 5419  sub _tree_construction_main ($) {
5419                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5420                                    
5421                  !!!next-token;                  !!!next-token;
5422                  redo B;                  next B;
5423                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5424                  !!!cp ('t177');                  !!!cp ('t177');
5425                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5426                                    text => $token->{tag_name}, token => $token);
5427                  ## Ignore the token                  ## Ignore the token
5428                  !!!next-token;                  !!!next-token;
5429                  redo B;                  next B;
5430                } else {                } else {
5431                  !!!cp ('t178');                  !!!cp ('t178');
5432                  #                  #
# Line 4046  sub _tree_construction_main ($) { Line 5439  sub _tree_construction_main ($) {
5439                ## have an element in table scope                ## have an element in table scope
5440                my $i;                my $i;
5441                my $tn;                my $tn;
5442                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5443                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5444                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5445                    !!!cp ('t179');                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5446                    $i = $_;                      !!!cp ('t179');
5447                    last INSCOPE;                      $i = $_;
5448                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
5449                    !!!cp ('t180');                      ## Close the cell
5450                    $tn = $node->[1];                      !!!back-token; # </x>
5451                    ## NOTE: There is exactly one |td| or |th| element                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5452                    ## in scope in the stack of open elements by definition.                                line => $token->{line},
5453                  } elsif ({                                column => $token->{column}};
5454                            table => 1, html => 1,                      next B;
5455                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_CELL_EL) {
5456                    !!!cp ('t181');                      !!!cp ('t180');
5457                    last INSCOPE;                      $tn = $node->[0]->manakai_local_name;
5458                        ## NOTE: There is exactly one |td| or |th| element
5459                        ## in scope in the stack of open elements by definition.
5460                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5461                        ## ISSUE: Can this be reached?
5462                        !!!cp ('t181');
5463                        last;
5464                      }
5465                  }                  }
5466                } # INSCOPE  
               unless (defined $i) {  
5467                  !!!cp ('t182');                  !!!cp ('t182');
5468                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5469                        text => $token->{tag_name}, token => $token);
5470                  ## Ignore the token                  ## Ignore the token
5471                  !!!next-token;                  !!!next-token;
5472                  redo B;                  next B;
5473                } else {                } # INSCOPE
                 !!!cp ('t183');  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5474              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5475                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5476                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5477                                  token => $token);
5478    
5479                ## As if </caption>                ## As if </caption>
5480                ## have a table element in table scope                ## have a table element in table scope
5481                my $i;                my $i;
5482                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5483                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5484                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5485                    !!!cp ('t184');                    !!!cp ('t184');
5486                    $i = $_;                    $i = $_;
5487                    last INSCOPE;                    last INSCOPE;
5488                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5489                    !!!cp ('t185');                    !!!cp ('t185');
5490                    last INSCOPE;                    last INSCOPE;
5491                  }                  }
5492                } # INSCOPE                } # INSCOPE
5493                unless (defined $i) {                unless (defined $i) {
5494                  !!!cp ('t186');                  !!!cp ('t186');
5495                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'unmatched end tag',
5496                                    text => 'caption', token => $token);
5497                  ## Ignore the token                  ## Ignore the token
5498                  !!!next-token;                  !!!next-token;
5499                  redo B;                  next B;
5500                }                }
5501                                
5502                ## generate implied end tags                ## generate implied end tags
5503                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5504                  !!!cp ('t187');                  !!!cp ('t187');
5505                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5506                }                }
5507    
5508                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5509                  !!!cp ('t188');                  !!!cp ('t188');
5510                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5511                                    text => $self->{open_elements}->[-1]->[0]
5512                                        ->manakai_local_name,
5513                                    token => $token);
5514                } else {                } else {
5515                  !!!cp ('t189');                  !!!cp ('t189');
5516                }                }
# Line 4128  sub _tree_construction_main ($) { Line 5522  sub _tree_construction_main ($) {
5522                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5523    
5524                ## reprocess                ## reprocess
5525                redo B;                next B;
5526              } elsif ({              } elsif ({
5527                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5528                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5529                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5530                  !!!cp ('t190');                  !!!cp ('t190');
5531                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5532                                    text => $token->{tag_name}, token => $token);
5533                  ## Ignore the token                  ## Ignore the token
5534                  !!!next-token;                  !!!next-token;
5535                  redo B;                  next B;
5536                } else {                } else {
5537                  !!!cp ('t191');                  !!!cp ('t191');
5538                  #                  #
# Line 4148  sub _tree_construction_main ($) { Line 5543  sub _tree_construction_main ($) {
5543                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5544                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5545                !!!cp ('t192');                !!!cp ('t192');
5546                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5547                                  text => $token->{tag_name}, token => $token);
5548                ## Ignore the token                ## Ignore the token
5549                !!!next-token;                !!!next-token;
5550                redo B;                next B;
5551              } else {              } else {
5552                !!!cp ('t193');                !!!cp ('t193');
5553                #                #
5554              }              }
5555          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5556            for my $entry (@{$self->{open_elements}}) {
5557              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5558                !!!cp ('t75');
5559                !!!parse-error (type => 'in body:#eof', token => $token);
5560                last;
5561              }
5562            }
5563    
5564            ## Stop parsing.
5565            last B;
5566        } else {        } else {
5567          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5568        }        }
# Line 4165  sub _tree_construction_main ($) { Line 5572  sub _tree_construction_main ($) {
5572      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5573        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5574          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5575              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5576            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5577                                
5578            unless (length $token->{data}) {            unless (length $token->{data}) {
5579              !!!cp ('t194');              !!!cp ('t194');
5580              !!!next-token;              !!!next-token;
5581              redo B;              next B;
5582            } else {            } else {
5583              !!!cp ('t195');              !!!cp ('t195');
5584            }            }
5585          }          }
5586    
5587              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5588    
5589              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5590              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5591                            
5592              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5593                   table => 1, tbody => 1, tfoot => 1,            # MUST
5594                   thead => 1, tr => 1,            my $foster_parent_element;
5595                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5596                # MUST            my $prev_sibling;
5597                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5598                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5599                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5600                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5601                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5602                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5603                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5604                      !!!cp ('t196');                  $prev_sibling = $next_sibling->previous_sibling;
5605                      $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});  
5606                } else {                } else {
5607                  !!!cp ('t199');                  !!!cp ('t197');
5608                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5609                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
5610                     $next_sibling);                  #
5611                }                }
5612                  last OE;
5613                }
5614              } # OE
5615              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5616              $prev_sibling = $foster_parent_element->last_child
5617                  unless defined $foster_parent_element;
5618              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5619              if (defined $prev_sibling and
5620                  $prev_sibling->node_type == 3) {
5621                !!!cp ('t198');
5622                $prev_sibling->manakai_append_text ($token->{data});
5623              } else {
5624                !!!cp ('t199');
5625                $foster_parent_element->insert_before
5626                    ($self->{document}->create_text_node ($token->{data}),
5627                     $next_sibling);
5628              }
5629            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
5630              $open_tables->[-1]->[2] = 1; # ~node inserted
5631          } else {          } else {
5632              ## NOTE: Fragment case or in a foster parent'ed element
5633              ## (e.g. |<table><span>a|).  In fragment case, whether the
5634              ## character is appended to existing node or a new node is
5635              ## created is irrelevant, since the foster parent'ed nodes
5636              ## are discarded and fragment parsing does not invoke any
5637              ## script.
5638            !!!cp ('t200');            !!!cp ('t200');
5639            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text
5640                  ($token->{data});
5641          }          }
5642                            
5643          !!!next-token;          !!!next-token;
5644          redo B;          next B;
5645        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5646              if ({          if ({
5647                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5648                   th => 1, td => 1,               th => 1, td => 1,
5649                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5650                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5651                  ## Clear back to table context              ## Clear back to table context
5652                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5653                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5654                    !!!cp ('t201');                !!!cp ('t201');
5655                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5656                  }              }
5657                                
5658                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5659                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5660                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5661                }            }
5662              
5663                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5664                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5665                    !!!cp ('t202');                !!!cp ('t202');
5666                    !!!parse-error (type => 'missing start tag:tr');                !!!parse-error (type => 'missing start tag:tr', token => $token);
5667                  }              }
5668                                    
5669                  ## Clear back to table body context              ## Clear back to table body context
5670                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5671                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5672                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5673                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5674                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5675                    pop @{$self->{open_elements}};              }
                 }  
5676                                    
5677                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5678                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5679                    !!!cp ('t204');                !!!cp ('t204');
5680                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5681                    !!!next-token;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5682                    redo B;                !!!nack ('t204');
5683                  } else {                !!!next-token;
5684                    !!!cp ('t205');                next B;
5685                    !!!insert-element ('tr');              } else {
5686                    ## reprocess in the "in row" insertion mode                !!!cp ('t205');
5687                  }                !!!insert-element ('tr',, $token);
5688                } else {                ## reprocess in the "in row" insertion mode
5689                  !!!cp ('t206');              }
5690                }            } else {
5691                !!!cp ('t206');
5692              }
5693    
5694                ## Clear back to table row context                ## Clear back to table row context
5695                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5696                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5697                  !!!cp ('t207');                  !!!cp ('t207');
5698                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5699                }                }
5700                                
5701                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5702                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5703              $self->{insertion_mode} = IN_CELL_IM;
5704    
5705                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5706                                
5707              !!!nack ('t207.1');
5708              !!!next-token;
5709              next B;
5710            } elsif ({
5711                      caption => 1, col => 1, colgroup => 1,
5712                      tbody => 1, tfoot => 1, thead => 1,
5713                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5714                     }->{$token->{tag_name}}) {
5715              if ($self->{insertion_mode} == IN_ROW_IM) {
5716                ## As if </tr>
5717                ## have an element in table scope
5718                my $i;
5719                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5720                  my $node = $self->{open_elements}->[$_];
5721                  if ($node->[1] & TABLE_ROW_EL) {
5722                    !!!cp ('t208');
5723                    $i = $_;
5724                    last INSCOPE;
5725                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5726                    !!!cp ('t209');
5727                    last INSCOPE;
5728                  }
5729                } # INSCOPE
5730                unless (defined $i) {
5731                  !!!cp ('t210');
5732                  ## TODO: This type is wrong.
5733                  !!!parse-error (type => 'unmacthed end tag',
5734                                  text => $token->{tag_name}, token => $token);
5735                  ## Ignore the token
5736                  !!!nack ('t210.1');
5737                !!!next-token;                !!!next-token;
5738                redo B;                next B;
5739              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] eq 'tr') {  
                     !!!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;  
                 }  
5740                                    
5741                  ## Clear back to table row context                  ## Clear back to table row context
5742                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5743                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5744                    !!!cp ('t211');                    !!!cp ('t211');
5745                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
5746                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4341  sub _tree_construction_main ($) { Line 5751  sub _tree_construction_main ($) {
5751                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5752                    !!!cp ('t212');                    !!!cp ('t212');
5753                    ## reprocess                    ## reprocess
5754                    redo B;                    !!!ack-later;
5755                      next B;
5756                  } else {                  } else {
5757                    !!!cp ('t213');                    !!!cp ('t213');
5758                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4353  sub _tree_construction_main ($) { Line 5764  sub _tree_construction_main ($) {
5764                  my $i;                  my $i;
5765                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5766                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5767                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5768                      !!!cp ('t214');                      !!!cp ('t214');
5769                      $i = $_;                      $i = $_;
5770                      last INSCOPE;                      last INSCOPE;
5771                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5772                      !!!cp ('t215');                      !!!cp ('t215');
5773                      last INSCOPE;                      last INSCOPE;
5774                    }                    }
5775                  } # INSCOPE                  } # INSCOPE
5776                  unless (defined $i) {                  unless (defined $i) {
5777                    !!!cp ('t216');                    !!!cp ('t216');
5778  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5779                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
5780                                      text => $token->{tag_name}, token => $token);
5781                    ## Ignore the token                    ## Ignore the token
5782                      !!!nack ('t216.1');
5783                    !!!next-token;                    !!!next-token;
5784                    redo B;                    next B;
5785                  }                  }
5786    
5787                  ## Clear back to table body context                  ## Clear back to table body context
5788                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5789                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5790                    !!!cp ('t217');                    !!!cp ('t217');
5791                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5792                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4398  sub _tree_construction_main ($) { Line 5806  sub _tree_construction_main ($) {
5806                  !!!cp ('t218');                  !!!cp ('t218');
5807                }                }
5808    
5809                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5810                  ## Clear back to table context              ## Clear back to table context
5811                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5812                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5813                    !!!cp ('t219');                !!!cp ('t219');
5814                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
5815                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5816                  }              }
5817                                
5818                  !!!insert-element ('colgroup');              !!!insert-element ('colgroup',, $token);
5819                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5820                  ## reprocess              ## reprocess
5821                  redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5822                } elsif ({              !!!ack-later;
5823                          caption => 1,              next B;
5824                          colgroup => 1,            } elsif ({
5825                          tbody => 1, tfoot => 1, thead => 1,                      caption => 1,
5826                         }->{$token->{tag_name}}) {                      colgroup => 1,
5827                  ## Clear back to table context                      tbody => 1, tfoot => 1, thead => 1,
5828                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                     }->{$token->{tag_name}}) {
5829                         $self->{open_elements}->[-1]->[1] ne 'html') {              ## Clear back to table context
5830                    while (not ($self->{open_elements}->[-1]->[1]
5831                                    & TABLE_SCOPING_EL)) {
5832                    !!!cp ('t220');                    !!!cp ('t220');
5833                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5834                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5835                  }                  }
5836                                    
5837                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5838                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5839                                    
5840                  !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5841                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5842                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5843                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5844                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5845                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5846                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5847                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5848                  !!!next-token;                                        }->{$token->{tag_name}};
5849                  redo B;              !!!next-token;
5850                } else {              !!!nack ('t220.1');
5851                  die "$0: in table: <>: $token->{tag_name}";              next B;
5852                }            } else {
5853                die "$0: in table: <>: $token->{tag_name}";
5854              }
5855              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5856                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5857                                  text => $self->{open_elements}->[-1]->[0]
5858                                      ->manakai_local_name,
5859                                  token => $token);
5860    
5861                ## As if </table>                ## As if </table>
5862                ## have a table element in table scope                ## have a table element in table scope
5863                my $i;                my $i;
5864                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5865                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5866                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5867                    !!!cp ('t221');                    !!!cp ('t221');
5868                    $i = $_;                    $i = $_;
5869                    last INSCOPE;                    last INSCOPE;
5870                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5871                    !!!cp ('t222');                    !!!cp ('t222');
5872                    last INSCOPE;                    last INSCOPE;
5873                  }                  }
# Line 4463  sub _tree_construction_main ($) { Line 5875  sub _tree_construction_main ($) {
5875                unless (defined $i) {                unless (defined $i) {
5876                  !!!cp ('t223');                  !!!cp ('t223');
5877  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5878                  !!!parse-error (type => 'unmatched end tag:table');                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5879                                    token => $token);
5880                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5881                    !!!nack ('t223.1');
5882                  !!!next-token;                  !!!next-token;
5883                  redo B;                  next B;
5884                }                }
5885                                
5886    ## TODO: Followings are removed from the latest spec.
5887                ## generate implied end tags                ## generate implied end tags
5888                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5889                  !!!cp ('t224');                  !!!cp ('t224');
5890                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5891                }                }
5892    
5893                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5894                  !!!cp ('t225');                  !!!cp ('t225');
5895  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5896                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5897                                    text => $self->{open_elements}->[-1]->[0]
5898                                        ->manakai_local_name,
5899                                    token => $token);
5900                } else {                } else {
5901                  !!!cp ('t226');                  !!!cp ('t226');
5902                }                }
# Line 4490  sub _tree_construction_main ($) { Line 5906  sub _tree_construction_main ($) {
5906    
5907                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5908    
5909                ## reprocess            ## reprocess
5910                redo B;            !!!ack-later;
5911              next B;
5912          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
5913            if (not $open_tables->[-1]->[1]) { # tainted            if (not $open_tables->[-1]->[1]) { # tainted
5914              !!!cp ('t227.8');              !!!cp ('t227.8');
5915              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5916              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5917              redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5918                next B;
5919            } else {            } else {
5920              !!!cp ('t227.7');              !!!cp ('t227.7');
5921              #              #
# Line 4507  sub _tree_construction_main ($) { Line 5925  sub _tree_construction_main ($) {
5925              !!!cp ('t227.6');              !!!cp ('t227.6');
5926              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5927              $script_start_tag->();              $script_start_tag->();
5928              redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5929                next B;
5930            } else {            } else {
5931              !!!cp ('t227.5');              !!!cp ('t227.5');
5932              #              #
# Line 4518  sub _tree_construction_main ($) { Line 5937  sub _tree_construction_main ($) {
5937                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5938                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5939                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5940                  !!!parse-error (type => 'in table:'.$token->{tag_name});                  !!!parse-error (type => 'in table',
5941                                    text => $token->{tag_name}, token => $token);
5942    
5943                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5944                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5945    
5946                  ## TODO: form element pointer                  ## TODO: form element pointer
5947    
5948                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5949    
5950                  !!!next-token;                  !!!next-token;
5951                  redo B;                  !!!ack ('t227.2.1');
5952                    next B;
5953                } else {                } else {
5954                  !!!cp ('t227.2');                  !!!cp ('t227.2');
5955                  #                  #
# Line 4545  sub _tree_construction_main ($) { Line 5967  sub _tree_construction_main ($) {
5967            #            #
5968          }          }
5969    
5970          !!!parse-error (type => 'in table:'.$token->{tag_name});          !!!parse-error (type => 'in table', text => $token->{tag_name},
5971                            token => $token);
5972    
5973          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5974          #          #
# Line 4556  sub _tree_construction_main ($) { Line 5979  sub _tree_construction_main ($) {
5979                my $i;                my $i;
5980                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5981                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5982                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5983                    !!!cp ('t228');                    !!!cp ('t228');
5984                    $i = $_;                    $i = $_;
5985                    last INSCOPE;                    last INSCOPE;
5986                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5987                    !!!cp ('t229');                    !!!cp ('t229');
5988                    last INSCOPE;                    last INSCOPE;
5989                  }                  }
5990                } # INSCOPE                } # INSCOPE
5991                unless (defined $i) {                unless (defined $i) {
5992                  !!!cp ('t230');                  !!!cp ('t230');
5993                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5994                                    text => $token->{tag_name}, token => $token);
5995                  ## Ignore the token                  ## Ignore the token
5996                    !!!nack ('t230.1');
5997                  !!!next-token;                  !!!next-token;
5998                  redo B;                  next B;
5999                } else {                } else {
6000                  !!!cp ('t232');                  !!!cp ('t232');
6001                }                }
6002    
6003                ## Clear back to table row context                ## Clear back to table row context
6004                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6005                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6006                  !!!cp ('t231');                  !!!cp ('t231');
6007  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6008                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4589  sub _tree_construction_main ($) { Line 6011  sub _tree_construction_main ($) {
6011                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6012                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6013                !!!next-token;                !!!next-token;
6014                redo B;                !!!nack ('t231.1');
6015                  next B;
6016              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6017                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6018                  ## As if </tr>                  ## As if </tr>
# Line 4597  sub _tree_construction_main ($) { Line 6020  sub _tree_construction_main ($) {
6020                  my $i;                  my $i;
6021                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6022                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6023                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6024                      !!!cp ('t233');                      !!!cp ('t233');
6025                      $i = $_;                      $i = $_;
6026                      last INSCOPE;                      last INSCOPE;
6027                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6028                      !!!cp ('t234');                      !!!cp ('t234');
6029                      last INSCOPE;                      last INSCOPE;
6030                    }                    }
# Line 4611  sub _tree_construction_main ($) { Line 6032  sub _tree_construction_main ($) {
6032                  unless (defined $i) {                  unless (defined $i) {
6033                    !!!cp ('t235');                    !!!cp ('t235');
6034  ## TODO: The following is wrong.  ## TODO: The following is wrong.
6035                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!parse-error (type => 'unmatched end tag',
6036                                      text => $token->{type}, token => $token);
6037                    ## Ignore the token                    ## Ignore the token
6038                      !!!nack ('t236.1');
6039                    !!!next-token;                    !!!next-token;
6040                    redo B;                    next B;
6041                  }                  }
6042                                    
6043                  ## Clear back to table row context                  ## Clear back to table row context
6044                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6045                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6046                    !!!cp ('t236');                    !!!cp ('t236');
6047  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6048                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4636  sub _tree_construction_main ($) { Line 6058  sub _tree_construction_main ($) {
6058                  my $i;                  my $i;
6059                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6060                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6061                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
6062                      !!!cp ('t237');                      !!!cp ('t237');
6063                      $i = $_;                      $i = $_;
6064                      last INSCOPE;                      last INSCOPE;
6065                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6066                      !!!cp ('t238');                      !!!cp ('t238');
6067                      last INSCOPE;                      last INSCOPE;
6068                    }                    }
6069                  } # INSCOPE                  } # INSCOPE
6070                  unless (defined $i) {                  unless (defined $i) {
6071                    !!!cp ('t239');                    !!!cp ('t239');
6072                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
6073                                      text => $token->{tag_name}, token => $token);
6074                    ## Ignore the token                    ## Ignore the token
6075                      !!!nack ('t239.1');
6076                    !!!next-token;                    !!!next-token;
6077                    redo B;                    next B;
6078                  }                  }
6079                                    
6080                  ## Clear back to table body context                  ## Clear back to table body context
6081                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6082                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6083                    !!!cp ('t240');                    !!!cp ('t240');
6084                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6085                  }                  }
# Line 4686  sub _tree_construction_main ($) { Line 6105  sub _tree_construction_main ($) {
6105                my $i;                my $i;
6106                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6107                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6108                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6109                    !!!cp ('t241');                    !!!cp ('t241');
6110                    $i = $_;                    $i = $_;
6111                    last INSCOPE;                    last INSCOPE;
6112                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6113                    !!!cp ('t242');                    !!!cp ('t242');
6114                    last INSCOPE;                    last INSCOPE;
6115                  }                  }
6116                } # INSCOPE                } # INSCOPE
6117                unless (defined $i) {                unless (defined $i) {
6118                  !!!cp ('t243');                  !!!cp ('t243');
6119                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6120                                    text => $token->{tag_name}, token => $token);
6121                  ## Ignore the token                  ## Ignore the token
6122                    !!!nack ('t243.1');
6123                  !!!next-token;                  !!!next-token;
6124                  redo B;                  next B;
6125                }                }
6126                                    
6127                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 4711  sub _tree_construction_main ($) { Line 6130  sub _tree_construction_main ($) {
6130                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6131                                
6132                !!!next-token;                !!!next-token;
6133                redo B;                next B;
6134              } elsif ({              } elsif ({
6135                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6136                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4721  sub _tree_construction_main ($) { Line 6140  sub _tree_construction_main ($) {
6140                  my $i;                  my $i;
6141                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6142                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6143                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6144                      !!!cp ('t247');                      !!!cp ('t247');
6145                      $i = $_;                      $i = $_;
6146                      last INSCOPE;                      last INSCOPE;
6147                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6148                      !!!cp ('t248');                      !!!cp ('t248');
6149                      last INSCOPE;                      last INSCOPE;
6150                    }                    }
6151                  } # INSCOPE                  } # INSCOPE
6152                    unless (defined $i) {                    unless (defined $i) {
6153                      !!!cp ('t249');                      !!!cp ('t249');
6154                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
6155                                        text => $token->{tag_name}, token => $token);
6156                      ## Ignore the token                      ## Ignore the token
6157                        !!!nack ('t249.1');
6158                      !!!next-token;                      !!!next-token;
6159                      redo B;                      next B;
6160                    }                    }
6161                                    
6162                  ## As if </tr>                  ## As if </tr>
# Line 4745  sub _tree_construction_main ($) { Line 6164  sub _tree_construction_main ($) {
6164                  my $i;                  my $i;
6165                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6166                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6167                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6168                      !!!cp ('t250');                      !!!cp ('t250');
6169                      $i = $_;                      $i = $_;
6170                      last INSCOPE;                      last INSCOPE;
6171                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6172                      !!!cp ('t251');                      !!!cp ('t251');
6173                      last INSCOPE;                      last INSCOPE;
6174                    }                    }
6175                  } # INSCOPE                  } # INSCOPE
6176                    unless (defined $i) {                    unless (defined $i) {
6177                      !!!cp ('t252');                      !!!cp ('t252');
6178                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!parse-error (type => 'unmatched end tag',
6179                                        text => 'tr', token => $token);
6180                      ## Ignore the token                      ## Ignore the token
6181                        !!!nack ('t252.1');
6182                      !!!next-token;                      !!!next-token;
6183                      redo B;                      next B;
6184                    }                    }
6185                                    
6186                  ## Clear back to table row context                  ## Clear back to table row context
6187                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6188                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6189                    !!!cp ('t253');                    !!!cp ('t253');
6190  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6191                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4782  sub _tree_construction_main ($) { Line 6200  sub _tree_construction_main ($) {
6200                my $i;                my $i;
6201                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6202                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6203                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6204                    !!!cp ('t254');                    !!!cp ('t254');
6205                    $i = $_;                    $i = $_;
6206                    last INSCOPE;                    last INSCOPE;
6207                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6208                    !!!cp ('t255');                    !!!cp ('t255');
6209                    last INSCOPE;                    last INSCOPE;
6210                  }                  }
6211                } # INSCOPE                } # INSCOPE
6212                unless (defined $i) {                unless (defined $i) {
6213                  !!!cp ('t256');                  !!!cp ('t256');
6214                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6215                                    text => $token->{tag_name}, token => $token);
6216                  ## Ignore the token                  ## Ignore the token
6217                    !!!nack ('t256.1');
6218                  !!!next-token;                  !!!next-token;
6219                  redo B;                  next B;
6220                }                }
6221    
6222                ## Clear back to table body context                ## Clear back to table body context
6223                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6224                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6225                  !!!cp ('t257');                  !!!cp ('t257');
6226  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6227                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4812  sub _tree_construction_main ($) { Line 6229  sub _tree_construction_main ($) {
6229    
6230                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6231                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6232                  !!!nack ('t257.1');
6233                !!!next-token;                !!!next-token;
6234                redo B;                next B;
6235              } elsif ({              } elsif ({
6236                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6237                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6238                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6239                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6240                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6241                !!!cp ('t258');            !!!cp ('t258');
6242                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6243                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6244                !!!next-token;            ## Ignore the token
6245                redo B;            !!!nack ('t258.1');
6246               !!!next-token;
6247              next B;
6248          } else {          } else {
6249            !!!cp ('t259');            !!!cp ('t259');
6250            !!!parse-error (type => 'in table:/'.$token->{tag_name});            !!!parse-error (type => 'in table:/',
6251                              text => $token->{tag_name}, token => $token);
6252    
6253            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6254            #            #
6255          }          }
6256          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6257            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6258                    @{$self->{open_elements}} == 1) { # redundant, maybe
6259              !!!parse-error (type => 'in body:#eof', token => $token);
6260              !!!cp ('t259.1');
6261              #
6262            } else {
6263              !!!cp ('t259.2');
6264              #
6265            }
6266    
6267            ## Stop parsing
6268            last B;
6269        } else {        } else {
6270          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6271        }        }
6272      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6273            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6274              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6275                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6276                unless (length $token->{data}) {                unless (length $token->{data}) {
6277                  !!!cp ('t260');                  !!!cp ('t260');
6278                  !!!next-token;                  !!!next-token;
6279                  redo B;                  next B;
6280                }                }
6281              }              }
6282                            
# Line 4851  sub _tree_construction_main ($) { Line 6285  sub _tree_construction_main ($) {
6285            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6286              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6287                !!!cp ('t262');                !!!cp ('t262');
6288                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6289                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6290                  !!!ack ('t262.1');
6291                !!!next-token;                !!!next-token;
6292                redo B;                next B;
6293              } else {              } else {
6294                !!!cp ('t263');                !!!cp ('t263');
6295                #                #
6296              }              }
6297            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6298              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6299                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6300                  !!!cp ('t264');                  !!!cp ('t264');
6301                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!parse-error (type => 'unmatched end tag',
6302                                    text => 'colgroup', token => $token);
6303                  ## Ignore the token                  ## Ignore the token
6304                  !!!next-token;                  !!!next-token;
6305                  redo B;                  next B;
6306                } else {                } else {
6307                  !!!cp ('t265');                  !!!cp ('t265');
6308                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6309                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6310                  !!!next-token;                  !!!next-token;
6311                  redo B;                              next B;            
6312                }                }
6313              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6314                !!!cp ('t266');                !!!cp ('t266');
6315                !!!parse-error (type => 'unmatched end tag:col');                !!!parse-error (type => 'unmatched end tag',
6316                                  text => 'col', token => $token);
6317                ## Ignore the token                ## Ignore the token
6318                !!!next-token;                !!!next-token;
6319                redo B;                next B;
6320              } else {              } else {
6321                !!!cp ('t267');                !!!cp ('t267');
6322                #                #
6323              }              }
6324            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6325              die "$0: $token->{type}: Unknown token type";          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6326            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6327              !!!cp ('t270.2');
6328              ## Stop parsing.
6329              last B;
6330            } else {
6331              ## NOTE: As if </colgroup>.
6332              !!!cp ('t270.1');
6333              pop @{$self->{open_elements}}; # colgroup
6334              $self->{insertion_mode} = IN_TABLE_IM;
6335              ## Reprocess.
6336              next B;
6337            }
6338          } else {
6339            die "$0: $token->{type}: Unknown token type";
6340          }
6341    
6342            ## As if </colgroup>            ## As if </colgroup>
6343            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6344              !!!cp ('t269');              !!!cp ('t269');
6345              !!!parse-error (type => 'unmatched end tag:colgroup');  ## TODO: Wrong error type?
6346                !!!parse-error (type => 'unmatched end tag',
6347                                text => 'colgroup', token => $token);
6348              ## Ignore the token              ## Ignore the token
6349                !!!nack ('t269.1');
6350              !!!next-token;              !!!next-token;
6351              redo B;              next B;
6352            } else {            } else {
6353              !!!cp ('t270');              !!!cp ('t270');
6354              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6355              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6356                !!!ack-later;
6357              ## reprocess              ## reprocess
6358              redo B;              next B;
6359            }            }
6360      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6361        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6362          !!!cp ('t271');          !!!cp ('t271');
6363          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6364          !!!next-token;          !!!next-token;
6365          redo B;          next B;
6366        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6367              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6368                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6369                  !!!cp ('t272');              !!!cp ('t272');
6370                  ## As if </option>              ## As if </option>
6371                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6372                } else {            } else {
6373                  !!!cp ('t273');              !!!cp ('t273');
6374                }            }
6375    
6376                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6377                !!!next-token;            !!!nack ('t273.1');
6378                redo B;            !!!next-token;
6379              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6380                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6381                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6382                  ## As if </option>              !!!cp ('t274');
6383                  pop @{$self->{open_elements}};              ## As if </option>
6384                } else {              pop @{$self->{open_elements}};
6385                  !!!cp ('t275');            } else {
6386                }              !!!cp ('t275');
6387              }
6388    
6389                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6390                  !!!cp ('t276');              !!!cp ('t276');
6391                  ## As if </optgroup>              ## As if </optgroup>
6392                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6393                } else {            } else {
6394                  !!!cp ('t277');              !!!cp ('t277');
6395                }            }
6396    
6397                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6398                !!!next-token;            !!!nack ('t277.1');
6399                redo B;            !!!next-token;
6400              } elsif ($token->{tag_name} eq 'select') {            next B;
6401  ## TODO: The type below is not good - <select> is replaced by </select>          } elsif ({
6402                !!!parse-error (type => 'not closed:select');                     select => 1, input => 1, textarea => 1,
6403                ## As if </select> instead                   }->{$token->{tag_name}} or
6404                ## have an element in table scope                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6405                my $i;                    {
6406                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                     caption => 1, table => 1,
6407                  my $node = $self->{open_elements}->[$_];                     tbody => 1, tfoot => 1, thead => 1,
6408                  if ($node->[1] eq $token->{tag_name}) {                     tr => 1, td => 1, th => 1,
6409                    !!!cp ('t278');                    }->{$token->{tag_name}})) {
6410                    $i = $_;            ## TODO: The type below is not good - <select> is replaced by </select>
6411                    last INSCOPE;            !!!parse-error (type => 'not closed', text => 'select',
6412                  } elsif ({                            token => $token);
6413                            table => 1, html => 1,            ## NOTE: As if the token were </select> (<select> case) or
6414                           }->{$node->[1]}) {            ## as if there were </select> (otherwise).
6415                    !!!cp ('t279');            ## have an element in table scope
6416                    last INSCOPE;            my $i;
6417                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6418                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6419                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
6420                  !!!cp ('t280');                !!!cp ('t278');
6421                  !!!parse-error (type => 'unmatched end tag:select');                $i = $_;
6422                  ## Ignore the token                last INSCOPE;
6423                  !!!next-token;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6424                  redo B;                !!!cp ('t279');
6425                }                last INSCOPE;
6426                }
6427              } # INSCOPE
6428              unless (defined $i) {
6429                !!!cp ('t280');
6430                !!!parse-error (type => 'unmatched end tag',
6431                                text => 'select', token => $token);
6432                ## Ignore the token
6433                !!!nack ('t280.1');
6434                !!!next-token;
6435                next B;
6436              }
6437                                
6438                !!!cp ('t281');            !!!cp ('t281');
6439                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6440    
6441                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6442    
6443                !!!next-token;            if ($token->{tag_name} eq 'select') {
6444                redo B;              !!!nack ('t281.2');
6445                !!!next-token;
6446                next B;
6447              } else {
6448                !!!cp ('t281.1');
6449                !!!ack-later;
6450                ## Reprocess the token.
6451                next B;
6452              }
6453          } else {          } else {
6454            !!!cp ('t282');            !!!cp ('t282');
6455            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!parse-error (type => 'in select',
6456                              text => $token->{tag_name}, token => $token);
6457            ## Ignore the token            ## Ignore the token
6458              !!!nack ('t282.1');
6459            !!!next-token;            !!!next-token;
6460            redo B;            next B;
6461          }          }
6462        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6463              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6464                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6465                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6466                  !!!cp ('t283');              !!!cp ('t283');
6467                  ## As if </option>              ## As if </option>
6468                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6469                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6470                  !!!cp ('t284');              !!!cp ('t284');
6471                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6472                } else {            } else {
6473                  !!!cp ('t285');              !!!cp ('t285');
6474                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6475                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6476                }              ## Ignore the token
6477                !!!next-token;            }
6478                redo B;            !!!nack ('t285.1');
6479              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6480                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6481                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6482                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6483                } else {              !!!cp ('t286');
6484                  !!!cp ('t287');              pop @{$self->{open_elements}};
6485                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            } else {
6486                  ## Ignore the token              !!!cp ('t287');
6487                }              !!!parse-error (type => 'unmatched end tag',
6488                !!!next-token;                              text => $token->{tag_name}, token => $token);
6489                redo B;              ## Ignore the token
6490              } elsif ($token->{tag_name} eq 'select') {            }
6491                ## have an element in table scope            !!!nack ('t287.1');
6492                my $i;            !!!next-token;
6493                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6494                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6495                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6496                    !!!cp ('t288');            my $i;
6497                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6498                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6499                  } elsif ({              if ($node->[1] & SELECT_EL) {
6500                            table => 1, html => 1,                !!!cp ('t288');
6501                           }->{$node->[1]}) {                $i = $_;
6502                    !!!cp ('t289');                last INSCOPE;
6503                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6504                  }                !!!cp ('t289');
6505                } # INSCOPE                last INSCOPE;
6506                unless (defined $i) {              }
6507                  !!!cp ('t290');            } # INSCOPE
6508                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            unless (defined $i) {
6509                  ## Ignore the token              !!!cp ('t290');
6510                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6511                  redo B;                              text => $token->{tag_name}, token => $token);
6512                }              ## Ignore the token
6513                !!!nack ('t290.1');
6514                !!!next-token;
6515                next B;
6516              }
6517                                
6518                !!!cp ('t291');            !!!cp ('t291');
6519                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6520    
6521                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6522    
6523                !!!next-token;            !!!nack ('t291.1');
6524                redo B;            !!!next-token;
6525              } elsif ({            next B;
6526                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6527                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6528                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6529                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6530                     }->{$token->{tag_name}}) {
6531  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6532                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6533                              text => $token->{tag_name}, token => $token);
6534                                
6535                ## have an element in table scope            ## have an element in table scope
6536                my $i;            my $i;
6537                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6538                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6539                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6540                    !!!cp ('t292');                !!!cp ('t292');
6541                    $i = $_;                $i = $_;
6542                    last INSCOPE;                last INSCOPE;
6543                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6544                            table => 1, html => 1,                !!!cp ('t293');
6545                           }->{$node->[1]}) {                last INSCOPE;
6546                    !!!cp ('t293');              }
6547                    last INSCOPE;            } # INSCOPE
6548                  }            unless (defined $i) {
6549                } # INSCOPE              !!!cp ('t294');
6550                unless (defined $i) {              ## Ignore the token
6551                  !!!cp ('t294');              !!!nack ('t294.1');
6552                  ## Ignore the token              !!!next-token;
6553                  !!!next-token;              next B;
6554                  redo B;            }
               }  
6555                                
6556                ## As if </select>            ## As if </select>
6557                ## have an element in table scope            ## have an element in table scope
6558                undef $i;            undef $i;
6559                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6560                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6561                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6562                    !!!cp ('t295');                !!!cp ('t295');
6563                    $i = $_;                $i = $_;
6564                    last INSCOPE;                last INSCOPE;
6565                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6566  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6567                    !!!cp ('t296');                !!!cp ('t296');
6568                    last INSCOPE;                last INSCOPE;
6569                  }              }
6570                } # INSCOPE            } # INSCOPE
6571                unless (defined $i) {            unless (defined $i) {
6572                  !!!cp ('t297');              !!!cp ('t297');
6573  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6574                  !!!parse-error (type => 'unmatched end tag:select');              !!!parse-error (type => 'unmatched end tag',
6575                  ## Ignore the </select> token                              text => 'select', token => $token);
6576                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6577                  redo B;              !!!nack ('t297.1');
6578                }              !!!next-token; ## TODO: ok?
6579                next B;
6580              }
6581                                
6582                !!!cp ('t298');            !!!cp ('t298');
6583                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6584    
6585                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6586    
6587                ## reprocess            !!!ack-later;
6588                redo B;            ## reprocess
6589              next B;
6590          } else {          } else {
6591            !!!cp ('t299');            !!!cp ('t299');
6592            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!parse-error (type => 'in select:/',
6593                              text => $token->{tag_name}, token => $token);
6594            ## Ignore the token            ## Ignore the token
6595              !!!nack ('t299.3');
6596            !!!next-token;            !!!next-token;
6597            redo B;            next B;
6598          }          }
6599          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6600            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6601                    @{$self->{open_elements}} == 1) { # redundant, maybe
6602              !!!cp ('t299.1');
6603              !!!parse-error (type => 'in body:#eof', token => $token);
6604            } else {
6605              !!!cp ('t299.2');
6606            }
6607    
6608            ## Stop parsing.
6609            last B;
6610        } else {        } else {
6611          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6612        }        }
6613      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6614        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6615          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6616            my $data = $1;            my $data = $1;
6617            ## As if in body            ## As if in body
6618            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5125  sub _tree_construction_main ($) { Line 6622  sub _tree_construction_main ($) {
6622            unless (length $token->{data}) {            unless (length $token->{data}) {
6623              !!!cp ('t300');              !!!cp ('t300');
6624              !!!next-token;              !!!next-token;
6625              redo B;              next B;
6626            }            }
6627          }          }
6628                    
6629          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6630            !!!cp ('t301');            !!!cp ('t301');
6631            !!!parse-error (type => 'after html:#character');            !!!parse-error (type => 'after html:#text', token => $token);
6632              #
           ## Reprocess in the "after body" insertion mode.  
6633          } else {          } else {
6634            !!!cp ('t302');            !!!cp ('t302');
6635              ## "after body" insertion mode
6636              !!!parse-error (type => 'after body:#text', token => $token);
6637              #
6638          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6639    
6640          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6641          ## reprocess          ## reprocess
6642          redo B;          next B;
6643        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6644          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6645            !!!cp ('t303');            !!!cp ('t303');
6646            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html',
6647                                        text => $token->{tag_name}, token => $token);
6648            ## Reprocess in the "after body" insertion mode.            #
6649          } else {          } else {
6650            !!!cp ('t304');            !!!cp ('t304');
6651              ## "after body" insertion mode
6652              !!!parse-error (type => 'after body',
6653                              text => $token->{tag_name}, token => $token);
6654              #
6655          }          }
6656    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6657          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6658            !!!ack-later;
6659          ## reprocess          ## reprocess
6660          redo B;          next B;
6661        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6662          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6663            !!!cp ('t305');            !!!cp ('t305');
6664            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/',
6665                              text => $token->{tag_name}, token => $token);
6666                        
6667            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6668            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6669              next B;
6670          } else {          } else {
6671            !!!cp ('t306');            !!!cp ('t306');
6672          }          }
# Line 5175  sub _tree_construction_main ($) { Line 6675  sub _tree_construction_main ($) {
6675          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6676            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6677              !!!cp ('t307');              !!!cp ('t307');
6678              !!!parse-error (type => 'unmatched end tag:html');              !!!parse-error (type => 'unmatched end tag',
6679                                text => 'html', token => $token);
6680              ## Ignore the token              ## Ignore the token
6681              !!!next-token;              !!!next-token;
6682              redo B;              next B;
6683            } else {            } else {
6684              !!!cp ('t308');              !!!cp ('t308');
6685              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6686              !!!next-token;              !!!next-token;
6687              redo B;              next B;
6688            }            }
6689          } else {          } else {
6690            !!!cp ('t309');            !!!cp ('t309');
6691            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!parse-error (type => 'after body:/',
6692                              text => $token->{tag_name}, token => $token);
6693    
6694            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6695            ## reprocess            ## reprocess
6696            redo B;            next B;
6697          }          }
6698          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6699            !!!cp ('t309.2');
6700            ## Stop parsing
6701            last B;
6702        } else {        } else {
6703          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6704        }        }
6705      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6706        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6707          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6708            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6709                        
6710            unless (length $token->{data}) {            unless (length $token->{data}) {
6711              !!!cp ('t310');              !!!cp ('t310');
6712              !!!next-token;              !!!next-token;
6713              redo B;              next B;
6714            }            }
6715          }          }
6716                    
6717          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6718            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6719              !!!cp ('t311');              !!!cp ('t311');
6720              !!!parse-error (type => 'in frameset:#character');              !!!parse-error (type => 'in frameset:#text', token => $token);
6721            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6722              !!!cp ('t312');              !!!cp ('t312');
6723              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#text', token => $token);
6724            } else { # "after html frameset"            } else { # "after after frameset"
6725              !!!cp ('t313');              !!!cp ('t313');
6726              !!!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');  
6727            }            }
6728                        
6729            ## Ignore the token.            ## Ignore the token.
# Line 5232  sub _tree_construction_main ($) { Line 6734  sub _tree_construction_main ($) {
6734              !!!cp ('t315');              !!!cp ('t315');
6735              !!!next-token;              !!!next-token;
6736            }            }
6737            redo B;            next B;
6738          }          }
6739                    
6740          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6741        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!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');  
         }  
   
6742          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6743              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6744            !!!cp ('t318');            !!!cp ('t318');
6745            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6746              !!!nack ('t318.1');
6747            !!!next-token;            !!!next-token;
6748            redo B;            next B;
6749          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6750                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6751            !!!cp ('t319');            !!!cp ('t319');
6752            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6753            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6754              !!!ack ('t319.1');
6755            !!!next-token;            !!!next-token;
6756            redo B;            next B;
6757          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6758            !!!cp ('t320');            !!!cp ('t320');
6759            ## NOTE: As if in body.            ## NOTE: As if in head.
6760            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6761            redo B;            next B;
6762    
6763              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6764              ## has no parse error.
6765          } else {          } else {
6766            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6767              !!!cp ('t321');              !!!cp ('t321');
6768              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!parse-error (type => 'in frameset',
6769            } else {                              text => $token->{tag_name}, token => $token);
6770              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6771              !!!cp ('t322');              !!!cp ('t322');
6772              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!parse-error (type => 'after frameset',
6773                                text => $token->{tag_name}, token => $token);
6774              } else { # "after after frameset"
6775                !!!cp ('t322.2');
6776                !!!parse-error (type => 'after after frameset',
6777                                text => $token->{tag_name}, token => $token);
6778            }            }
6779            ## Ignore the token            ## Ignore the token
6780              !!!nack ('t322.1');
6781            !!!next-token;            !!!next-token;
6782            redo B;            next B;
6783          }          }
6784        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!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');  
         }  
   
6785          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6786              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6787            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6788                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6789              !!!cp ('t325');              !!!cp ('t325');
6790              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6791                                text => $token->{tag_name}, token => $token);
6792              ## Ignore the token              ## Ignore the token
6793              !!!next-token;              !!!next-token;
6794            } else {            } else {
# Line 5303  sub _tree_construction_main ($) { Line 6798  sub _tree_construction_main ($) {
6798            }            }
6799    
6800            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6801                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6802              !!!cp ('t327');              !!!cp ('t327');
6803              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6804            } else {            } else {
6805              !!!cp ('t328');              !!!cp ('t328');
6806            }            }
6807            redo B;            next B;
6808          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6809                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6810            !!!cp ('t329');            !!!cp ('t329');
6811            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6812            !!!next-token;            !!!next-token;
6813            redo B;            next B;
6814          } else {          } else {
6815            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6816              !!!cp ('t330');              !!!cp ('t330');
6817              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!parse-error (type => 'in frameset:/',
6818            } else {                              text => $token->{tag_name}, token => $token);
6819              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6820                !!!cp ('t330.1');
6821                !!!parse-error (type => 'after frameset:/',
6822                                text => $token->{tag_name}, token => $token);
6823              } else { # "after after html"
6824              !!!cp ('t331');              !!!cp ('t331');
6825              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!parse-error (type => 'after after frameset:/',
6826                                text => $token->{tag_name}, token => $token);
6827            }            }
6828            ## Ignore the token            ## Ignore the token
6829            !!!next-token;            !!!next-token;
6830            redo B;            next B;
6831          }          }
6832          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6833            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6834                    @{$self->{open_elements}} == 1) { # redundant, maybe
6835              !!!cp ('t331.1');
6836              !!!parse-error (type => 'in body:#eof', token => $token);
6837            } else {
6838              !!!cp ('t331.2');
6839            }
6840            
6841            ## Stop parsing
6842            last B;
6843        } else {        } else {
6844          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6845        }        }
   
       ## ISSUE: An issue in spec here  
6846      } else {      } else {
6847        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6848      }      }
# Line 5343  sub _tree_construction_main ($) { Line 6853  sub _tree_construction_main ($) {
6853          !!!cp ('t332');          !!!cp ('t332');
6854          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6855          $script_start_tag->();          $script_start_tag->();
6856          redo B;          next B;
6857        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6858          !!!cp ('t333');          !!!cp ('t333');
6859          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6860          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6861          redo B;          next B;
6862        } elsif ({        } elsif ({
6863                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6864                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6865          !!!cp ('t334');          !!!cp ('t334');
6866          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6867          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6868          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6869            !!!ack ('t334.1');
6870          !!!next-token;          !!!next-token;
6871          redo B;          next B;
6872        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6873          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6874          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6875          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6876    
6877          unless ($self->{confident}) {          unless ($self->{confident}) {
6878            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6879              !!!cp ('t335');              !!!cp ('t335');
6880                ## NOTE: Whether the encoding is supported or not is handled
6881                ## in the {change_encoding} callback.
6882              $self->{change_encoding}              $self->{change_encoding}
6883                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6884                            
6885              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6886                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6887                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6888                                           ->{has_reference});                                           ->{has_reference});
6889            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6890              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6891                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6892                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6893                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6894                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6895                       /x) {
6896                !!!cp ('t336');                !!!cp ('t336');
6897                  ## NOTE: Whether the encoding is supported or not is handled
6898                  ## in the {change_encoding} callback.
6899                $self->{change_encoding}                $self->{change_encoding}
6900                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6901                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6902                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6903                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 5406  sub _tree_construction_main ($) { Line 6921  sub _tree_construction_main ($) {
6921            }            }
6922          }          }
6923    
6924            !!!ack ('t338.1');
6925          !!!next-token;          !!!next-token;
6926          redo B;          next B;
6927        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6928          !!!cp ('t341');          !!!cp ('t341');
6929          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6930          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6931          redo B;          next B;
6932        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6933          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6934                                
6935          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6936              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6937            !!!cp ('t342');            !!!cp ('t342');
6938            ## Ignore the token            ## Ignore the token
6939          } else {          } else {
# Line 5431  sub _tree_construction_main ($) { Line 6947  sub _tree_construction_main ($) {
6947              }              }
6948            }            }
6949          }          }
6950            !!!nack ('t343.1');
6951          !!!next-token;          !!!next-token;
6952          redo B;          next B;
6953        } elsif ({        } elsif ({
6954                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6955                  div => 1, dl => 1, fieldset => 1,  
6956                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6957                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6958                    center => 1, datagrid => 1, details => 1, dialog => 1,
6959                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6960                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6961                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6962                    section => 1, ul => 1,
6963                    ## NOTE: As normal, but drops leading newline
6964                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6965                    ## NOTE: As normal, but interacts with the form element pointer
6966                    form => 1,
6967                    
6968                    table => 1,
6969                    hr => 1,
6970                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6971            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6972              !!!cp ('t350');
6973              !!!parse-error (type => 'in form:form', token => $token);
6974              ## Ignore the token
6975              !!!nack ('t350.1');
6976              !!!next-token;
6977              next B;
6978            }
6979    
6980          ## has a p element in scope          ## has a p element in scope
6981          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6982            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6983              !!!cp ('t344');              !!!cp ('t344');
6984              !!!back-token;              !!!back-token; # <form>
6985              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6986              redo B;                        line => $token->{line}, column => $token->{column}};
6987            } elsif ({              next B;
6988                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6989              !!!cp ('t345');              !!!cp ('t345');
6990              last INSCOPE;              last INSCOPE;
6991            }            }
6992          } # INSCOPE          } # INSCOPE
6993                        
6994          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6995          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6996              !!!nack ('t346.1');
6997            !!!next-token;            !!!next-token;
6998            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6999              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5470  sub _tree_construction_main ($) { Line 7006  sub _tree_construction_main ($) {
7006            } else {            } else {
7007              !!!cp ('t348');              !!!cp ('t348');
7008            }            }
7009          } else {          } elsif ($token->{tag_name} eq 'form') {
7010            !!!cp ('t347');            !!!cp ('t347.1');
7011              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7012    
7013              !!!nack ('t347.2');
7014            !!!next-token;            !!!next-token;
7015          }          } elsif ($token->{tag_name} eq 'table') {
7016          redo B;            !!!cp ('t382');
7017        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7018          if (defined $self->{form_element}) {            
7019            !!!cp ('t350');            $self->{insertion_mode} = IN_TABLE_IM;
7020            !!!parse-error (type => 'in form:form');  
7021            ## Ignore the token            !!!nack ('t382.1');
7022              !!!next-token;
7023            } elsif ($token->{tag_name} eq 'hr') {
7024              !!!cp ('t386');
7025              pop @{$self->{open_elements}};
7026            
7027              !!!nack ('t386.1');
7028            !!!next-token;            !!!next-token;
           redo B;  
7029          } else {          } else {
7030            ## has a p element in scope            !!!nack ('t347.1');
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!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];  
7031            !!!next-token;            !!!next-token;
           redo B;  
7032          }          }
7033            next B;
7034        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
7035          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7036          INSCOPE: for (reverse @{$self->{open_elements}}) {  
7037            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7038              !!!cp ('t353');            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7039              !!!back-token;            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7040              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7041              redo B;            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7042            } elsif ({            ## object (Fx)
7043                      table => 1, caption => 1, td => 1, th => 1,            ## Generate non-tree (non-conforming)
7044                      button => 1, marquee => 1, object => 1, html => 1,            ## basefont (IE7 (where basefont is non-void)), center (IE),
7045                     }->{$_->[1]}) {            ## form (IE), hn (IE)
7046              !!!cp ('t354');          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7047              last INSCOPE;            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7048            }            ## div (Fx, S)
7049          } # INSCOPE  
7050                      my $non_optional;
         ## Step 1  
7051          my $i = -1;          my $i = -1;
7052          my $node = $self->{open_elements}->[$i];  
7053          LI: {          ## 1.
7054            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7055            if ($node->[1] eq 'li') {            if ($node->[1] & LI_EL) {
7056              if ($i != -1) {              ## 2. (a) As if </li>
7057                !!!cp ('t355');              {
7058                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7059                                $self->{open_elements}->[-1]->[1]);                #
7060              } else {  
7061                !!!cp ('t356');                ## Otherwise
7062    
7063                  ## 1. generate implied end tags, except for </li>
7064                  #
7065    
7066                  ## 2. If current node != "li", parse error
7067                  if ($non_optional) {
7068                    !!!parse-error (type => 'not closed',
7069                                    text => $non_optional->[0]->manakai_local_name,
7070                                    token => $token);
7071                    !!!cp ('t355');
7072                  } else {
7073                    !!!cp ('t356');
7074                  }
7075    
7076                  ## 3. Pop
7077                  splice @{$self->{open_elements}}, $i;
7078              }              }
7079              splice @{$self->{open_elements}}, $i;  
7080              last LI;              last; ## 2. (b) goto 5.
7081            } else {            } elsif (
7082                       ## NOTE: not "formatting" and not "phrasing"
7083                       ($node->[1] & SPECIAL_EL or
7084                        $node->[1] & SCOPING_EL) and
7085                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7086    
7087                       (not $node->[1] & ADDRESS_EL) &
7088                       (not $node->[1] & DIV_EL) &
7089                       (not $node->[1] & P_EL)) {
7090                ## 3.
7091              !!!cp ('t357');              !!!cp ('t357');
7092            }              last; ## goto 5.
7093                        } 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') {  
7094              !!!cp ('t358');              !!!cp ('t358');
7095              last LI;              #
7096              } else {
7097                !!!cp ('t359');
7098                $non_optional ||= $node;
7099                #
7100            }            }
7101                        ## 4.
7102            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7103            $i--;            $i--;
7104            $node = $self->{open_elements}->[$i];          }
7105            redo LI;  
7106          } # LI          ## 5. (a) has a |p| element in scope
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
7107          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7108            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7109              !!!cp ('t360');              !!!cp ('t353');
7110              !!!back-token;  
7111              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              ## NOTE: |<p><li>|, for example.
7112              redo B;  
7113            } elsif ({              !!!back-token; # <x>
7114                      table => 1, caption => 1, td => 1, th => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7115                      button => 1, marquee => 1, object => 1, html => 1,                        line => $token->{line}, column => $token->{column}};
7116                     }->{$_->[1]}) {              next B;
7117              !!!cp ('t361');            } elsif ($_->[1] & SCOPING_EL) {
7118                !!!cp ('t354');
7119              last INSCOPE;              last INSCOPE;
7120            }            }
7121          } # INSCOPE          } # INSCOPE
7122              
7123          ## Step 1          ## 5. (b) insert
7124            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7125            !!!nack ('t359.1');
7126            !!!next-token;
7127            next B;
7128          } elsif ($token->{tag_name} eq 'dt' or
7129                   $token->{tag_name} eq 'dd') {
7130            ## NOTE: As normal, but imply </dt> or </dd> when ...
7131    
7132            my $non_optional;
7133          my $i = -1;          my $i = -1;
7134          my $node = $self->{open_elements}->[$i];  
7135          LI: {          ## 1.
7136            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7137            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7138              if ($i != -1) {              ## 2. (a) As if </li>
7139                !!!cp ('t362');              {
7140                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7141                                $self->{open_elements}->[-1]->[1]);                #
7142              } else {  
7143                !!!cp ('t363');                ## Otherwise
7144    
7145                  ## 1. generate implied end tags, except for </dt> or </dd>
7146                  #
7147    
7148                  ## 2. If current node != "dt"|"dd", parse error
7149                  if ($non_optional) {
7150                    !!!parse-error (type => 'not closed',
7151                                    text => $non_optional->[0]->manakai_local_name,
7152                                    token => $token);
7153                    !!!cp ('t355.1');
7154                  } else {
7155                    !!!cp ('t356.1');
7156                  }
7157    
7158                  ## 3. Pop
7159                  splice @{$self->{open_elements}}, $i;
7160              }              }
7161              splice @{$self->{open_elements}}, $i;  
7162              last LI;              last; ## 2. (b) goto 5.
7163              } elsif (
7164                       ## NOTE: not "formatting" and not "phrasing"
7165                       ($node->[1] & SPECIAL_EL or
7166                        $node->[1] & SCOPING_EL) and
7167                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7168    
7169                       (not $node->[1] & ADDRESS_EL) &
7170                       (not $node->[1] & DIV_EL) &
7171                       (not $node->[1] & P_EL)) {
7172                ## 3.
7173                !!!cp ('t357.1');
7174                last; ## goto 5.
7175              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7176                !!!cp ('t358.1');
7177                #
7178            } else {            } else {
7179              !!!cp ('t364');              !!!cp ('t359.1');
7180            }              $non_optional ||= $node;
7181                          #
           ## 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;  
7182            }            }
7183                        ## 4.
7184            !!!cp ('t366');            ## goto 2.
           ## Step 4  
7185            $i--;            $i--;
7186            $node = $self->{open_elements}->[$i];          }
7187            redo LI;  
7188          } # LI          ## 5. (a) has a |p| element in scope
7189                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7190          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            if ($_->[1] & P_EL) {
7191                !!!cp ('t353.1');
7192                !!!back-token; # <x>
7193                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7194                          line => $token->{line}, column => $token->{column}};
7195                next B;
7196              } elsif ($_->[1] & SCOPING_EL) {
7197                !!!cp ('t354.1');
7198                last INSCOPE;
7199              }
7200            } # INSCOPE
7201    
7202            ## 5. (b) insert
7203            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7204            !!!nack ('t359.2');
7205          !!!next-token;          !!!next-token;
7206          redo B;          next B;
7207        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7208            ## NOTE: As normal, but effectively ends parsing
7209    
7210          ## has a p element in scope          ## has a p element in scope
7211          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7212            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7213              !!!cp ('t367');              !!!cp ('t367');
7214              !!!back-token;              !!!back-token; # <plaintext>
7215              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7216              redo B;                        line => $token->{line}, column => $token->{column}};
7217            } elsif ({              next B;
7218                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7219              !!!cp ('t368');              !!!cp ('t368');
7220              last INSCOPE;              last INSCOPE;
7221            }            }
7222          } # INSCOPE          } # INSCOPE
7223                        
7224          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7225                        
7226          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7227                        
7228            !!!nack ('t368.1');
7229          !!!next-token;          !!!next-token;
7230          redo B;          next B;
7231        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7232          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7233            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7234            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7235              !!!cp ('t371');              !!!cp ('t371');
7236              !!!parse-error (type => 'in a:a');              !!!parse-error (type => 'in a:a', token => $token);
7237                            
7238              !!!back-token;              !!!back-token; # <a>
7239              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7240              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7241                $formatting_end_tag->($token);
7242                            
7243              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7244                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
# Line 5673  sub _tree_construction_main ($) { Line 7263  sub _tree_construction_main ($) {
7263                        
7264          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7265    
7266          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7267          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7268    
7269            !!!nack ('t374.1');
7270          !!!next-token;          !!!next-token;
7271          redo B;          next B;
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         !!!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;  
7272        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7273          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7274    
7275          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7276          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7277            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7278            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7279              !!!cp ('t376');              !!!cp ('t376');
7280              !!!parse-error (type => 'in nobr:nobr');              !!!parse-error (type => 'in nobr:nobr', token => $token);
7281              !!!back-token;              !!!back-token; # <nobr>
7282              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7283              redo B;                        line => $token->{line}, column => $token->{column}};
7284            } elsif ({              next B;
7285                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7286              !!!cp ('t377');              !!!cp ('t377');
7287              last INSCOPE;              last INSCOPE;
7288            }            }
7289          } # INSCOPE          } # INSCOPE
7290                    
7291          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7292          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7293                    
7294            !!!nack ('t377.1');
7295          !!!next-token;          !!!next-token;
7296          redo B;          next B;
7297        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7298          ## has a button element in scope          ## has a button element in scope
7299          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7300            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7301            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7302              !!!cp ('t378');              !!!cp ('t378');
7303              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button', token => $token);
7304              !!!back-token;              !!!back-token; # <button>
7305              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
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 ('t379');              !!!cp ('t379');
7310              last INSCOPE;              last INSCOPE;
7311            }            }
# Line 5738  sub _tree_construction_main ($) { Line 7313  sub _tree_construction_main ($) {
7313                        
7314          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7315                        
7316          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7317    
7318          ## TODO: associate with $self->{form_element} if defined          ## TODO: associate with $self->{form_element} if defined
7319    
7320          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7321    
7322            !!!nack ('t379.1');
7323          !!!next-token;          !!!next-token;
7324          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);  
         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});  
         push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];  
   
         $self->{insertion_mode} = IN_TABLE_IM;  
             
         !!!next-token;  
         redo B;  
7325        } elsif ({        } elsif ({
7326                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7327                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7328                  image => 1,                  noembed => 1,
7329                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7330                    noscript => 0, ## TODO: 1 if scripting is enabled
7331                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7332          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7333            !!!cp ('t384');            !!!cp ('t381');
7334            !!!parse-error (type => 'image');            $reconstruct_active_formatting_elements->($insert_to_current);
           $token->{tag_name} = 'img';  
7335          } else {          } else {
7336            !!!cp ('t385');            !!!cp ('t399');
7337          }          }
7338            ## NOTE: There is an "as if in body" code clone.
7339          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7340          $reconstruct_active_formatting_elements->($insert_to_current);          next B;
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!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;  
7341        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7342          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7343                    
7344          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7345            !!!cp ('t389');            !!!cp ('t389');
7346            ## Ignore the token            ## Ignore the token
7347              !!!nack ('t389'); ## NOTE: Not acknowledged.
7348            !!!next-token;            !!!next-token;
7349            redo B;            next B;
7350          } else {          } else {
7351              !!!ack ('t391.1');
7352    
7353            my $at = $token->{attributes};            my $at = $token->{attributes};
7354            my $form_attrs;            my $form_attrs;
7355            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5856  sub _tree_construction_main ($) { Line 7359  sub _tree_construction_main ($) {
7359            delete $at->{prompt};            delete $at->{prompt};
7360            my @tokens = (            my @tokens = (
7361                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7362                           attributes => $form_attrs},                           attributes => $form_attrs,
7363                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7364                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7365                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7366                            {type => START_TAG_TOKEN, tag_name => 'p',
7367                             line => $token->{line}, column => $token->{column}},
7368                            {type => START_TAG_TOKEN, tag_name => 'label',
7369                             line => $token->{line}, column => $token->{column}},
7370                         );                         );
7371            if ($prompt_attr) {            if ($prompt_attr) {
7372              !!!cp ('t390');              !!!cp ('t390');
7373              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7374                               #line => $token->{line}, column => $token->{column},
7375                              };
7376            } else {            } else {
7377              !!!cp ('t391');              !!!cp ('t391');
7378              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7379                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7380                               #line => $token->{line}, column => $token->{column},
7381                              }; # SHOULD
7382              ## TODO: make this configurable              ## TODO: make this configurable
7383            }            }
7384            push @tokens,            push @tokens,
7385                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7386                             line => $token->{line}, column => $token->{column}},
7387                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7388                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7389                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7390                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7391                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7392            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7393                             line => $token->{line}, column => $token->{column}},
7394                            {type => END_TAG_TOKEN, tag_name => 'form',
7395                             line => $token->{line}, column => $token->{column}};
7396            !!!back-token (@tokens);            !!!back-token (@tokens);
7397            redo B;            !!!next-token;
7398              next B;
7399          }          }
7400        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7401          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7402          my $el;          my $el;
7403          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7404                    
7405          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7406          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5893  sub _tree_construction_main ($) { Line 7409  sub _tree_construction_main ($) {
7409          $insert->($el);          $insert->($el);
7410                    
7411          my $text = '';          my $text = '';
7412            !!!nack ('t392.1');
7413          !!!next-token;          !!!next-token;
7414          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7415            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
# Line 5923  sub _tree_construction_main ($) { Line 7440  sub _tree_construction_main ($) {
7440            ## Ignore the token            ## Ignore the token
7441          } else {          } else {
7442            !!!cp ('t398');            !!!cp ('t398');
7443            !!!parse-error (type => 'in RCDATA:#'.$token->{type});            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7444          }          }
7445          !!!next-token;          !!!next-token;
7446            next B;
7447          } elsif ($token->{tag_name} eq 'optgroup' or
7448                   $token->{tag_name} eq 'option') {
7449            ## has an |option| element in scope
7450            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7451              my $node = $self->{open_elements}->[$_];
7452              if ($node->[1] & OPTION_EL) {
7453                !!!cp ('t397.1');
7454                ## NOTE: As if </option>
7455                !!!back-token; # <option> or <optgroup>
7456                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7457                          line => $token->{line}, column => $token->{column}};
7458                next B;
7459              } elsif ($node->[1] & SCOPING_EL) {
7460                !!!cp ('t397.2');
7461                last INSCOPE;
7462              }
7463            } # INSCOPE
7464    
7465            $reconstruct_active_formatting_elements->($insert_to_current);
7466    
7467            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7468    
7469            !!!nack ('t397.3');
7470            !!!next-token;
7471          redo B;          redo B;
7472        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7473                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7474                  noembed => 1,          ## has a |ruby| element in scope
7475                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7476                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7477                 }->{$token->{tag_name}}) {            if ($node->[1] & RUBY_EL) {
7478          !!!cp ('t399');              !!!cp ('t398.1');
7479          ## NOTE: There is an "as if in body" code clone.              ## generate implied end tags
7480          $parse_rcdata->(CDATA_CONTENT_MODEL);              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7481                  !!!cp ('t398.2');
7482                  pop @{$self->{open_elements}};
7483                }
7484                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7485                  !!!cp ('t398.3');
7486                  !!!parse-error (type => 'not closed',
7487                                  text => $self->{open_elements}->[-1]->[0]
7488                                      ->manakai_local_name,
7489                                  token => $token);
7490                  pop @{$self->{open_elements}}
7491                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7492                }
7493                last INSCOPE;
7494              } elsif ($node->[1] & SCOPING_EL) {
7495                !!!cp ('t398.4');
7496                last INSCOPE;
7497              }
7498            } # INSCOPE
7499    
7500            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7501    
7502            !!!nack ('t398.5');
7503            !!!next-token;
7504          redo B;          redo B;
7505        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7506          !!!cp ('t400');                 $token->{tag_name} eq 'svg') {
7507          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
7508    
7509          ## TODO: associate with $self->{form_element} if defined          ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7510    
7511            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7512    
7513            ## "adjust foreign attributes" - done in insert-element-f
7514            
7515            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7516                    
7517          $self->{insertion_mode} = IN_SELECT_IM;          if ($self->{self_closing}) {
7518              pop @{$self->{open_elements}};
7519              !!!ack ('t398.6');
7520            } else {
7521              !!!cp ('t398.7');
7522              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7523              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7524              ## mode, "in body" (not "in foreign content") secondary insertion
7525              ## mode, maybe.
7526            }
7527    
7528          !!!next-token;          !!!next-token;
7529          redo B;          next B;
7530        } elsif ({        } elsif ({
7531                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7532                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7533                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7534                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7535                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7536          !!!cp ('t401');          !!!cp ('t401');
7537          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'in body',
7538                            text => $token->{tag_name}, token => $token);
7539          ## Ignore the token          ## Ignore the token
7540            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7541            !!!next-token;
7542            next B;
7543          } elsif ($token->{tag_name} eq 'param' or
7544                   $token->{tag_name} eq 'source') {
7545            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7546            pop @{$self->{open_elements}};
7547    
7548            !!!ack ('t398.5');
7549          !!!next-token;          !!!next-token;
7550          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7551        } else {        } else {
7552          !!!cp ('t402');          if ($token->{tag_name} eq 'image') {
7553              !!!cp ('t384');
7554              !!!parse-error (type => 'image', token => $token);
7555              $token->{tag_name} = 'img';
7556            } else {
7557              !!!cp ('t385');
7558            }
7559    
7560            ## NOTE: There is an "as if <br>" code clone.
7561          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7562                    
7563          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7564    
7565            if ({
7566                 applet => 1, marquee => 1, object => 1,
7567                }->{$token->{tag_name}}) {
7568              !!!cp ('t380');
7569              push @$active_formatting_elements, ['#marker', ''];
7570              !!!nack ('t380.1');
7571            } elsif ({
7572                      b => 1, big => 1, em => 1, font => 1, i => 1,
7573                      s => 1, small => 1, strike => 1,
7574                      strong => 1, tt => 1, u => 1,
7575                     }->{$token->{tag_name}}) {
7576              !!!cp ('t375');
7577              push @$active_formatting_elements, $self->{open_elements}->[-1];
7578              !!!nack ('t375.1');
7579            } elsif ($token->{tag_name} eq 'input') {
7580              !!!cp ('t388');
7581              ## TODO: associate with $self->{form_element} if defined
7582              pop @{$self->{open_elements}};
7583              !!!ack ('t388.2');
7584            } elsif ({
7585                      area => 1, basefont => 1, bgsound => 1, br => 1,
7586                      embed => 1, img => 1, spacer => 1, wbr => 1,
7587                     }->{$token->{tag_name}}) {
7588              !!!cp ('t388.1');
7589              pop @{$self->{open_elements}};
7590              !!!ack ('t388.3');
7591            } elsif ($token->{tag_name} eq 'select') {
7592              ## TODO: associate with $self->{form_element} if defined
7593            
7594              if ($self->{insertion_mode} & TABLE_IMS or
7595                  $self->{insertion_mode} & BODY_TABLE_IMS or
7596                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7597                !!!cp ('t400.1');
7598                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7599              } else {
7600                !!!cp ('t400.2');
7601                $self->{insertion_mode} = IN_SELECT_IM;
7602              }
7603              !!!nack ('t400.3');
7604            } else {
7605              !!!nack ('t402');
7606            }
7607                    
7608          !!!next-token;          !!!next-token;
7609          redo B;          next B;
7610        }        }
7611      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7612        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7613          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7614              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7615            for (@{$self->{open_elements}}) {          INSCOPE: {
7616              unless ({            for (reverse @{$self->{open_elements}}) {
7617                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
7618                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7619                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7620                      }->{$_->[1]}) {                last INSCOPE;
7621                !!!cp ('t403');              } elsif ($_->[1] & SCOPING_EL) {
7622                !!!parse-error (type => 'not closed:'.$_->[1]);                !!!cp ('t405.1');
7623              } else {                last;
               !!!cp ('t404');  
7624              }              }
7625            }            }
7626    
7627            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7628            !!!next-token;  
7629            redo B;            !!!parse-error (type => 'unmatched end tag',
7630          } else {                            text => $token->{tag_name}, token => $token);
7631            !!!cp ('t405');            ## NOTE: Ignore the token.
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
7632            !!!next-token;            !!!next-token;
7633            redo B;            next B;
7634            } # INSCOPE
7635    
7636            for (@{$self->{open_elements}}) {
7637              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7638                !!!cp ('t403');
7639                !!!parse-error (type => 'not closed',
7640                                text => $_->[0]->manakai_local_name,
7641                                token => $token);
7642                last;
7643              } else {
7644                !!!cp ('t404');
7645              }
7646          }          }
7647    
7648            $self->{insertion_mode} = AFTER_BODY_IM;
7649            !!!next-token;
7650            next B;
7651        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7652          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {          ## TODO: Update this code.  It seems that the code below is not
7653            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7654            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7655                $self->{open_elements}->[1]->[1] & BODY_EL) {
7656              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7657              !!!cp ('t406');              !!!cp ('t406');
7658              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!parse-error (type => 'not closed',
7659                                text => $self->{open_elements}->[1]->[0]
7660                                    ->manakai_local_name,
7661                                token => $token);
7662            } else {            } else {
7663              !!!cp ('t407');              !!!cp ('t407');
7664            }            }
7665            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7666            ## reprocess            ## reprocess
7667            redo B;            next B;
7668          } else {          } else {
7669            !!!cp ('t408');            !!!cp ('t408');
7670            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7671                              text => $token->{tag_name}, token => $token);
7672            ## Ignore the token            ## Ignore the token
7673            !!!next-token;            !!!next-token;
7674            redo B;            next B;
7675          }          }
7676        } elsif ({        } elsif ({
7677                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7678                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7679                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7680                    address => 1, article => 1, aside => 1, blockquote => 1,
7681                    center => 1, datagrid => 1, details => 1, dialog => 1,
7682                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7683                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7684                    ol => 1, pre => 1, section => 1, ul => 1,
7685    
7686                    ## NOTE: As normal, but ... optional tags
7687                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7688                  button => 1, marquee => 1, object => 1,  
7689                    applet => 1, button => 1, marquee => 1, object => 1,
7690                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7691            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7692            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7693            ## </dd>" code.
7694    
7695          ## has an element in scope          ## has an element in scope
7696          my $i;          my $i;
7697          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7698            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7699            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7700              !!!cp ('t410');              !!!cp ('t410');
7701              $i = $_;              $i = $_;
7702              last INSCOPE;              last INSCOPE;
7703            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7704              !!!cp ('t411');              !!!cp ('t411');
7705              last INSCOPE;              last INSCOPE;
7706            }            }
# Line 6042  sub _tree_construction_main ($) { Line 7708  sub _tree_construction_main ($) {
7708    
7709          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7710            !!!cp ('t413');            !!!cp ('t413');
7711            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7712                              text => $token->{tag_name}, token => $token);
7713              ## NOTE: Ignore the token.
7714          } else {          } else {
7715            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7716            while ({            while ({
7717                      ## END_TAG_OPTIONAL_EL
7718                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7719                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7720                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7721                      option => 1,
7722                      optgroup => 1,
7723                    p => 1,                    p => 1,
7724                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7725                      rp => 1,
7726                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7727              !!!cp ('t409');              !!!cp ('t409');
7728              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7729            }            }
7730    
7731            ## Step 2.            ## Step 2.
7732            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7733                      ne $token->{tag_name}) {
7734              !!!cp ('t412');              !!!cp ('t412');
7735              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7736                                text => $self->{open_elements}->[-1]->[0]
7737                                    ->manakai_local_name,
7738                                token => $token);
7739            } else {            } else {
7740              !!!cp ('t414');              !!!cp ('t414');
7741            }            }
# Line 6069  sub _tree_construction_main ($) { Line 7746  sub _tree_construction_main ($) {
7746            ## Step 4.            ## Step 4.
7747            $clear_up_to_marker->()            $clear_up_to_marker->()
7748                if {                if {
7749                  button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7750                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7751          }          }
7752          !!!next-token;          !!!next-token;
7753          redo B;          next B;
7754        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7755            ## NOTE: As normal, but interacts with the form element pointer
7756    
7757          undef $self->{form_element};          undef $self->{form_element};
7758    
7759          ## has an element in scope          ## has an element in scope
7760          my $i;          my $i;
7761          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7762            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7763            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7764              !!!cp ('t418');              !!!cp ('t418');
7765              $i = $_;              $i = $_;
7766              last INSCOPE;              last INSCOPE;
7767            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7768              !!!cp ('t419');              !!!cp ('t419');
7769              last INSCOPE;              last INSCOPE;
7770            }            }
# Line 6096  sub _tree_construction_main ($) { Line 7772  sub _tree_construction_main ($) {
7772    
7773          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7774            !!!cp ('t421');            !!!cp ('t421');
7775            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7776                              text => $token->{tag_name}, token => $token);
7777              ## NOTE: Ignore the token.
7778          } else {          } else {
7779            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7780            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7781              !!!cp ('t417');              !!!cp ('t417');
7782              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7783            }            }
7784                        
7785            ## Step 2.            ## Step 2.
7786            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7787                      ne $token->{tag_name}) {
7788              !!!cp ('t417.1');              !!!cp ('t417.1');
7789              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7790                                text => $self->{open_elements}->[-1]->[0]
7791                                    ->manakai_local_name,
7792                                token => $token);
7793            } else {            } else {
7794              !!!cp ('t420');              !!!cp ('t420');
7795            }              }  
# Line 6119  sub _tree_construction_main ($) { Line 7799  sub _tree_construction_main ($) {
7799          }          }
7800    
7801          !!!next-token;          !!!next-token;
7802          redo B;          next B;
7803        } elsif ({        } elsif ({
7804                    ## NOTE: As normal, except acts as a closer for any ...
7805                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7806                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7807          ## has an element in scope          ## has an element in scope
7808          my $i;          my $i;
7809          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7810            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7811            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7812              !!!cp ('t423');              !!!cp ('t423');
7813              $i = $_;              $i = $_;
7814              last INSCOPE;              last INSCOPE;
7815            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7816              !!!cp ('t424');              !!!cp ('t424');
7817              last INSCOPE;              last INSCOPE;
7818            }            }
# Line 6144  sub _tree_construction_main ($) { Line 7820  sub _tree_construction_main ($) {
7820    
7821          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7822            !!!cp ('t425.1');            !!!cp ('t425.1');
7823            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7824                              text => $token->{tag_name}, token => $token);
7825              ## NOTE: Ignore the token.
7826          } else {          } else {
7827            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7828            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7829              !!!cp ('t422');              !!!cp ('t422');
7830              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7831            }            }
7832                        
7833            ## Step 2.            ## Step 2.
7834            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7835                      ne $token->{tag_name}) {
7836              !!!cp ('t425');              !!!cp ('t425');
7837              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
7838                                text => $token->{tag_name}, token => $token);
7839            } else {            } else {
7840              !!!cp ('t426');              !!!cp ('t426');
7841            }            }
# Line 6167  sub _tree_construction_main ($) { Line 7845  sub _tree_construction_main ($) {
7845          }          }
7846                    
7847          !!!next-token;          !!!next-token;
7848          redo B;          next B;
7849        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7850            ## NOTE: As normal, except </p> implies <p> and ...
7851    
7852          ## has an element in scope          ## has an element in scope
7853            my $non_optional;
7854          my $i;          my $i;
7855          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7856            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7857            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & P_EL) {
7858              !!!cp ('t410.1');              !!!cp ('t410.1');
7859              $i = $_;              $i = $_;
7860              last INSCOPE;              last INSCOPE;
7861            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7862              !!!cp ('t411.1');              !!!cp ('t411.1');
7863              last INSCOPE;              last INSCOPE;
7864              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7865                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7866                !!!cp ('t411.2');
7867                #
7868              } else {
7869                !!!cp ('t411.3');
7870                $non_optional ||= $node;
7871                #
7872            }            }
7873          } # INSCOPE          } # INSCOPE
7874    
7875          if (defined $i) {          if (defined $i) {
7876            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            ## 1. Generate implied end tags
7877              #
7878    
7879              ## 2. If current node != "p", parse error
7880              if ($non_optional) {
7881              !!!cp ('t412.1');              !!!cp ('t412.1');
7882              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7883                                text => $non_optional->[0]->manakai_local_name,
7884                                token => $token);
7885            } else {            } else {
7886              !!!cp ('t414.1');              !!!cp ('t414.1');
7887            }            }
7888    
7889              ## 3. Pop
7890            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7891          } else {          } else {
7892            !!!cp ('t413.1');            !!!cp ('t413.1');
7893            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7894                              text => $token->{tag_name}, token => $token);
7895    
7896            !!!cp ('t415.1');            !!!cp ('t415.1');
7897            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7898            my $el;            my $el;
7899            !!!create-element ($el, 'p');            !!!create-element ($el, $HTML_NS, 'p',, $token);
7900            $insert->($el);            $insert->($el);
7901            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7902          }          }
7903    
7904          !!!next-token;          !!!next-token;
7905          redo B;          next B;
7906        } elsif ({        } elsif ({
7907                  a => 1,                  a => 1,
7908                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7909                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7910                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7911                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7912          !!!cp ('t427');          !!!cp ('t427');
7913          $formatting_end_tag->($token->{tag_name});          $formatting_end_tag->($token);
7914          redo B;          next B;
7915        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7916          !!!cp ('t428');          !!!cp ('t428');
7917          !!!parse-error (type => 'unmatched end tag:br');          !!!parse-error (type => 'unmatched end tag',
7918                            text => 'br', token => $token);
7919    
7920          ## As if <br>          ## As if <br>
7921          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7922                    
7923          my $el;          my $el;
7924          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7925          $insert->($el);          $insert->($el);
7926                    
7927          ## Ignore the token.          ## Ignore the token.
7928          !!!next-token;          !!!next-token;
7929          redo B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!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  
           
7930        } else {        } else {
7931            if ($token->{tag_name} eq 'sarcasm') {
7932              sleep 0.001; # take a deep breath
7933            }
7934    
7935          ## Step 1          ## Step 1
7936          my $node_i = -1;          my $node_i = -1;
7937          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7938    
7939          ## Step 2          ## Step 2
7940          S2: {          S2: {
7941            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7942              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7943              if ($node_tag_name eq $token->{tag_name}) {
7944              ## Step 1              ## Step 1
7945              ## generate implied end tags              ## generate implied end tags
7946              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7947                !!!cp ('t430');                !!!cp ('t430');
7948                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7949                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7950                  ## which seems wrong.
7951                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7952                  $node_i++;
7953              }              }
7954                    
7955              ## Step 2              ## Step 2
7956              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7957                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7958                $current_tag_name =~ tr/A-Z/a-z/;
7959                if ($current_tag_name ne $token->{tag_name}) {
7960                !!!cp ('t431');                !!!cp ('t431');
7961                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7962                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7963                                  text => $self->{open_elements}->[-1]->[0]
7964                                      ->manakai_local_name,
7965                                  token => $token);
7966              } else {              } else {
7967                !!!cp ('t432');                !!!cp ('t432');
7968              }              }
7969                            
7970              ## Step 3              ## Step 3
7971              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7972    
7973              !!!next-token;              !!!next-token;
7974              last S2;              last S2;
7975            } else {            } else {
7976              ## Step 3              ## Step 3
7977              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7978                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7979                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7980                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7981                !!!cp ('t433');                !!!cp ('t433');
7982                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
7983                                  text => $token->{tag_name}, token => $token);
7984                ## Ignore the token                ## Ignore the token
7985                !!!next-token;                !!!next-token;
7986                last S2;                last S2;
             }  
7987    
7988                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7989                  ## 9.27, "a" is a child of <dd> (conforming).  In
7990                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7991                  ## "a" is a child of both <body> and <dd>.
7992                }
7993                
7994              !!!cp ('t434');              !!!cp ('t434');
7995            }            }
7996                        
# Line 6307  sub _tree_construction_main ($) { Line 8001  sub _tree_construction_main ($) {
8001            ## Step 5;            ## Step 5;
8002            redo S2;            redo S2;
8003          } # S2          } # S2
8004          redo B;          next B;
8005        }        }
8006      }      }
8007      redo B;      next B;
8008      } continue { # B
8009        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8010          ## NOTE: The code below is executed in cases where it does not have
8011          ## to be, but it it is harmless even in those cases.
8012          ## has an element in scope
8013          INSCOPE: {
8014            for (reverse 0..$#{$self->{open_elements}}) {
8015              my $node = $self->{open_elements}->[$_];
8016              if ($node->[1] & FOREIGN_EL) {
8017                last INSCOPE;
8018              } elsif ($node->[1] & SCOPING_EL) {
8019                last;
8020              }
8021            }
8022            
8023            ## NOTE: No foreign element in scope.
8024            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8025          } # INSCOPE
8026        }
8027    } # B    } # B
8028    
8029    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6318  sub _tree_construction_main ($) { Line 8031  sub _tree_construction_main ($) {
8031    ## TODO: script stuffs    ## TODO: script stuffs
8032  } # _tree_construct_main  } # _tree_construct_main
8033    
8034  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8035    my $class = shift;    my $class = shift;
8036    my $node = shift;    my $node = shift;
8037    my $s = \$_[0];    #my $s = \$_[0];
8038    my $onerror = $_[1];    my $onerror = $_[1];
8039      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8040    
8041    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
8042    
# Line 6341  sub set_inner_html ($$$) { Line 8055  sub set_inner_html ($$$) {
8055      }      }
8056    
8057      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8058      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8059    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8060      ## TODO: If non-html element      ## TODO: If non-html element
8061    
8062      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8063    
8064    ## TODO: Support for $get_wrapper
8065    
8066      ## Step 1 # MUST      ## Step 1 # MUST
8067      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8068      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6356  sub set_inner_html ($$$) { Line 8072  sub set_inner_html ($$$) {
8072    
8073      ## Step 8 # MUST      ## Step 8 # MUST
8074      my $i = 0;      my $i = 0;
8075      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8076      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8077      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8078        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8079        $input = $get_wrapper->($input);
8080        $p->{set_nc} = sub {
8081        my $self = shift;        my $self = shift;
8082    
8083        pop @{$self->{prev_char}};        my $char = '';
8084        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8085            $char = $self->{next_nc};
8086            delete $self->{next_nc};
8087            $self->{nc} = ord $char;
8088          } else {
8089            $self->{char_buffer} = '';
8090            $self->{char_buffer_pos} = 0;
8091            
8092            my $count = $input->manakai_read_until
8093                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8094                 $self->{char_buffer_pos});
8095            if ($count) {
8096              $self->{line_prev} = $self->{line};
8097              $self->{column_prev} = $self->{column};
8098              $self->{column}++;
8099              $self->{nc}
8100                  = ord substr ($self->{char_buffer},
8101                                $self->{char_buffer_pos}++, 1);
8102              return;
8103            }
8104            
8105            if ($input->read ($char, 1)) {
8106              $self->{nc} = ord $char;
8107            } else {
8108              $self->{nc} = -1;
8109              return;
8110            }
8111          }
8112    
8113        $self->{next_char} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8114        $self->{next_char} = ord substr $$s, $i++, 1;        $p->{column}++;
8115        $column++;  
8116          if ($self->{nc} == 0x000A) { # LF
8117        if ($self->{next_char} == 0x000A) { # LF          $p->{line}++;
8118          $line++;          $p->{column} = 0;
         $column = 0;  
8119          !!!cp ('i1');          !!!cp ('i1');
8120        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8121          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8122          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8123          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8124          $column = 0;            $self->{next_nc} = $next;
8125            }
8126            $self->{nc} = 0x000A; # LF # MUST
8127            $p->{line}++;
8128            $p->{column} = 0;
8129          !!!cp ('i2');          !!!cp ('i2');
8130        } 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  
8131          !!!cp ('i4');          !!!cp ('i4');
8132          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8133          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8134        }        }
8135      };      };
8136      $p->{prev_char} = [-1, -1, -1];  
8137      $p->{next_char} = -1;      $p->{read_until} = sub {
8138              #my ($scalar, $specials_range, $offset) = @_;
8139          return 0 if defined $p->{next_nc};
8140    
8141          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8142          my $offset = $_[2] || 0;
8143          
8144          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8145            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8146            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8147              substr ($_[0], $offset)
8148                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8149              my $count = $+[0] - $-[0];
8150              if ($count) {
8151                $p->{column} += $count;
8152                $p->{char_buffer_pos} += $count;
8153                $p->{line_prev} = $p->{line};
8154                $p->{column_prev} = $p->{column} - 1;
8155                $p->{nc} = -1;
8156              }
8157              return $count;
8158            } else {
8159              return 0;
8160            }
8161          } else {
8162            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8163            if ($count) {
8164              $p->{column} += $count;
8165              $p->{column_prev} += $count;
8166              $p->{nc} = -1;
8167            }
8168            return $count;
8169          }
8170        }; # $p->{read_until}
8171    
8172      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8173        my (%opt) = @_;        my (%opt) = @_;
8174        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8175          my $column = $opt{column};
8176          if (defined $opt{token} and defined $opt{token}->{line}) {
8177            $line = $opt{token}->{line};
8178            $column = $opt{token}->{column};
8179          }
8180          warn "Parse error ($opt{type}) at line $line column $column\n";
8181      };      };
8182      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8183        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8184      };      };
8185            
8186        my $char_onerror = sub {
8187          my (undef, $type, %opt) = @_;
8188          $ponerror->(layer => 'encode',
8189                      line => $p->{line}, column => $p->{column} + 1,
8190                      %opt, type => $type);
8191        }; # $char_onerror
8192        $input->onerror ($char_onerror);
8193    
8194      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8195      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8196    
# Line 6419  sub set_inner_html ($$$) { Line 8212  sub set_inner_html ($$$) {
8212          unless defined $p->{content_model};          unless defined $p->{content_model};
8213          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8214    
8215      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8216          ## TODO: Foreign element OK?
8217    
8218      ## Step 3      ## Step 3
8219      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6429  sub set_inner_html ($$$) { Line 8223  sub set_inner_html ($$$) {
8223      $doc->append_child ($root);      $doc->append_child ($root);
8224    
8225      ## Step 5 # MUST      ## Step 5 # MUST
8226      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8227    
8228      undef $p->{head_element};      undef $p->{head_element};
8229        undef $p->{head_element_inserted};
8230    
8231      ## Step 6 # MUST      ## Step 6 # MUST
8232      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
# Line 6475  sub set_inner_html ($$$) { Line 8270  sub set_inner_html ($$$) {
8270      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8271    
8272      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8273    
8274        delete $p->{parse_error}; # delete loop
8275    } else {    } else {
8276      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
8277    }    }

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24