/[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.102 by wakaba, Sun Mar 9 06:29:25 2008 UTC revision 1.206 by wakaba, Mon Oct 13 08:22:30 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)  require IO::Handle;
23  ## TODO: 1252 parse error (revision 1264)  
24  ## TODO: 8859-11 = 874 (revision 1271)  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  my $permitted_slash_tag_name = {  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    base => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    link => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    meta => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    hr => 1,  
31    br => 1,  ## Bits 12-15
32    img => 1,  sub SPECIAL_EL () { 0b1_000000000000000 }
33    embed => 1,  sub SCOPING_EL () { 0b1_00000000000000 }
34    param => 1,  sub FORMATTING_EL () { 0b1_0000000000000 }
35    area => 1,  sub PHRASING_EL () { 0b1_000000000000 }
36    col => 1,  
37    input => 1,  ## Bits 10-11
38    sub FOREIGN_EL () { 0b1_00000000000 }
39    sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
40    
41    ## Bits 6-9
42    sub TABLE_SCOPING_EL () { 0b1_000000000 }
43    sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
44    sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
45    sub TABLE_ROWS_EL () { 0b1_000000 }
46    
47    ## Bit 5
48    sub ADDRESS_DIV_P_EL () { 0b1_00000 }
49    
50    ## NOTE: Used in </body> and EOF algorithms.
51    ## Bit 4
52    sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
53    
54    ## NOTE: Used in "generate implied end tags" algorithm.
55    ## NOTE: There is a code where a modified version of
56    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
57    ## implementation (search for the algorithm name).
58    ## Bit 3
59    sub END_TAG_OPTIONAL_EL () { 0b1_000 }
60    
61    ## Bits 0-2
62    
63    sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
64    sub FORM_EL () { SPECIAL_EL | 0b001 }
65    sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
66    sub HEADING_EL () { SPECIAL_EL | 0b011 }
67    sub SELECT_EL () { SPECIAL_EL | 0b100 }
68    sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
69    
70    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
71    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
72    
73    sub DD_EL () {
74      SPECIAL_EL |
75      END_TAG_OPTIONAL_EL |
76      ALL_END_TAG_OPTIONAL_EL |
77      0b001
78    }
79    sub DT_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84    }
85    sub LI_EL () {
86      SPECIAL_EL |
87      END_TAG_OPTIONAL_EL |
88      ALL_END_TAG_OPTIONAL_EL |
89      0b100
90    }
91    sub P_EL () {
92      SPECIAL_EL |
93      ADDRESS_DIV_P_EL |
94      END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97    }
98    
99    sub TABLE_ROW_EL () {
100      SPECIAL_EL |
101      TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112    }
113    
114    sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    sub HTML_EL () {
118      SCOPING_EL |
119      TABLE_SCOPING_EL |
120      TABLE_ROWS_SCOPING_EL |
121      TABLE_ROW_SCOPING_EL |
122      ALL_END_TAG_OPTIONAL_EL |
123      0b001
124    }
125    sub TABLE_EL () {
126      SCOPING_EL |
127      TABLE_ROWS_EL |
128      TABLE_SCOPING_EL |
129      0b001
130    }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151    my $el_category = {
152      a => A_EL,
153      address => ADDRESS_DIV_EL,
154      applet => MISC_SCOPING_EL,
155      area => MISC_SPECIAL_EL,
156      article => MISC_SPECIAL_EL,
157      aside => MISC_SPECIAL_EL,
158      b => FORMATTING_EL,
159      base => MISC_SPECIAL_EL,
160      basefont => MISC_SPECIAL_EL,
161      bgsound => MISC_SPECIAL_EL,
162      big => FORMATTING_EL,
163      blockquote => MISC_SPECIAL_EL,
164      body => BODY_EL,
165      br => MISC_SPECIAL_EL,
166      button => BUTTON_EL,
167      caption => CAPTION_EL,
168      center => MISC_SPECIAL_EL,
169      col => MISC_SPECIAL_EL,
170      colgroup => MISC_SPECIAL_EL,
171      command => MISC_SPECIAL_EL,
172      datagrid => MISC_SPECIAL_EL,
173      dd => DD_EL,
174      details => MISC_SPECIAL_EL,
175      dialog => MISC_SPECIAL_EL,
176      dir => MISC_SPECIAL_EL,
177      div => ADDRESS_DIV_EL,
178      dl => MISC_SPECIAL_EL,
179      dt => DT_EL,
180      em => FORMATTING_EL,
181      embed => MISC_SPECIAL_EL,
182      eventsource => MISC_SPECIAL_EL,
183      fieldset => MISC_SPECIAL_EL,
184      figure => MISC_SPECIAL_EL,
185      font => FORMATTING_EL,
186      footer => MISC_SPECIAL_EL,
187      form => FORM_EL,
188      frame => MISC_SPECIAL_EL,
189      frameset => FRAMESET_EL,
190      h1 => HEADING_EL,
191      h2 => HEADING_EL,
192      h3 => HEADING_EL,
193      h4 => HEADING_EL,
194      h5 => HEADING_EL,
195      h6 => HEADING_EL,
196      head => MISC_SPECIAL_EL,
197      header => MISC_SPECIAL_EL,
198      hr => MISC_SPECIAL_EL,
199      html => HTML_EL,
200      i => FORMATTING_EL,
201      iframe => MISC_SPECIAL_EL,
202      img => MISC_SPECIAL_EL,
203      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
204      input => MISC_SPECIAL_EL,
205      isindex => MISC_SPECIAL_EL,
206      li => LI_EL,
207      link => MISC_SPECIAL_EL,
208      listing => MISC_SPECIAL_EL,
209      marquee => MISC_SCOPING_EL,
210      menu => MISC_SPECIAL_EL,
211      meta => MISC_SPECIAL_EL,
212      nav => MISC_SPECIAL_EL,
213      nobr => NOBR_EL,
214      noembed => MISC_SPECIAL_EL,
215      noframes => MISC_SPECIAL_EL,
216      noscript => MISC_SPECIAL_EL,
217      object => MISC_SCOPING_EL,
218      ol => MISC_SPECIAL_EL,
219      optgroup => OPTGROUP_EL,
220      option => OPTION_EL,
221      p => P_EL,
222      param => MISC_SPECIAL_EL,
223      plaintext => MISC_SPECIAL_EL,
224      pre => MISC_SPECIAL_EL,
225      rp => RUBY_COMPONENT_EL,
226      rt => RUBY_COMPONENT_EL,
227      ruby => RUBY_EL,
228      s => FORMATTING_EL,
229      script => MISC_SPECIAL_EL,
230      select => SELECT_EL,
231      section => MISC_SPECIAL_EL,
232      small => FORMATTING_EL,
233      spacer => MISC_SPECIAL_EL,
234      strike => FORMATTING_EL,
235      strong => FORMATTING_EL,
236      style => MISC_SPECIAL_EL,
237      table => TABLE_EL,
238      tbody => TABLE_ROW_GROUP_EL,
239      td => TABLE_CELL_EL,
240      textarea => MISC_SPECIAL_EL,
241      tfoot => TABLE_ROW_GROUP_EL,
242      th => TABLE_CELL_EL,
243      thead => TABLE_ROW_GROUP_EL,
244      title => MISC_SPECIAL_EL,
245      tr => TABLE_ROW_EL,
246      tt => FORMATTING_EL,
247      u => FORMATTING_EL,
248      ul => MISC_SPECIAL_EL,
249      wbr => MISC_SPECIAL_EL,
250    };
251    
252    my $el_category_f = {
253      $MML_NS => {
254        'annotation-xml' => MML_AXML_EL,
255        mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
256        mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257        mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258        ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259        mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260      },
261      $SVG_NS => {
262        foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
263        desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
264        title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265      },
266      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
267    };
268    
269    my $svg_attr_name = {
270      attributename => 'attributeName',
271      attributetype => 'attributeType',
272      basefrequency => 'baseFrequency',
273      baseprofile => 'baseProfile',
274      calcmode => 'calcMode',
275      clippathunits => 'clipPathUnits',
276      contentscripttype => 'contentScriptType',
277      contentstyletype => 'contentStyleType',
278      diffuseconstant => 'diffuseConstant',
279      edgemode => 'edgeMode',
280      externalresourcesrequired => 'externalResourcesRequired',
281      filterres => 'filterRes',
282      filterunits => 'filterUnits',
283      glyphref => 'glyphRef',
284      gradienttransform => 'gradientTransform',
285      gradientunits => 'gradientUnits',
286      kernelmatrix => 'kernelMatrix',
287      kernelunitlength => 'kernelUnitLength',
288      keypoints => 'keyPoints',
289      keysplines => 'keySplines',
290      keytimes => 'keyTimes',
291      lengthadjust => 'lengthAdjust',
292      limitingconeangle => 'limitingConeAngle',
293      markerheight => 'markerHeight',
294      markerunits => 'markerUnits',
295      markerwidth => 'markerWidth',
296      maskcontentunits => 'maskContentUnits',
297      maskunits => 'maskUnits',
298      numoctaves => 'numOctaves',
299      pathlength => 'pathLength',
300      patterncontentunits => 'patternContentUnits',
301      patterntransform => 'patternTransform',
302      patternunits => 'patternUnits',
303      pointsatx => 'pointsAtX',
304      pointsaty => 'pointsAtY',
305      pointsatz => 'pointsAtZ',
306      preservealpha => 'preserveAlpha',
307      preserveaspectratio => 'preserveAspectRatio',
308      primitiveunits => 'primitiveUnits',
309      refx => 'refX',
310      refy => 'refY',
311      repeatcount => 'repeatCount',
312      repeatdur => 'repeatDur',
313      requiredextensions => 'requiredExtensions',
314      requiredfeatures => 'requiredFeatures',
315      specularconstant => 'specularConstant',
316      specularexponent => 'specularExponent',
317      spreadmethod => 'spreadMethod',
318      startoffset => 'startOffset',
319      stddeviation => 'stdDeviation',
320      stitchtiles => 'stitchTiles',
321      surfacescale => 'surfaceScale',
322      systemlanguage => 'systemLanguage',
323      tablevalues => 'tableValues',
324      targetx => 'targetX',
325      targety => 'targetY',
326      textlength => 'textLength',
327      viewbox => 'viewBox',
328      viewtarget => 'viewTarget',
329      xchannelselector => 'xChannelSelector',
330      ychannelselector => 'yChannelSelector',
331      zoomandpan => 'zoomAndPan',
332  };  };
333    
334  my $c1_entity_char = {  my $foreign_attr_xname = {
335      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
336      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
337      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
338      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
339      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
340      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
341      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
342      'xml:base' => [$XML_NS, ['xml', 'base']],
343      'xml:lang' => [$XML_NS, ['xml', 'lang']],
344      'xml:space' => [$XML_NS, ['xml', 'space']],
345      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
346      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
347    };
348    
349    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
350    
351    my $charref_map = {
352      0x0D => 0x000A,
353    0x80 => 0x20AC,    0x80 => 0x20AC,
354    0x81 => 0xFFFD,    0x81 => 0xFFFD,
355    0x82 => 0x201A,    0x82 => 0x201A,
# Line 59  my $c1_entity_char = { Line 382  my $c1_entity_char = {
382    0x9D => 0xFFFD,    0x9D => 0xFFFD,
383    0x9E => 0x017E,    0x9E => 0x017E,
384    0x9F => 0x0178,    0x9F => 0x0178,
385  }; # $c1_entity_char  }; # $charref_map
386    $charref_map->{$_} = 0xFFFD
387        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
388            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
389            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
390            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
391            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
392            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
393            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
394    
395  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
396    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
397    
398  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
399      my $self = shift;
400      my $charset_name = shift;
401      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
402      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
403    } # parse_byte_string
404    
405    sub parse_byte_stream ($$$$;$$) {
406      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
407    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
408    my $charset = shift;    my $charset_name = shift;
409    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
   my $s;  
     
   if (defined $charset) {  
     require Encode; ## TODO: decode(utf8) don't delete BOM  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = lc $charset; ## TODO: normalize name  
     $self->{confident} = 1;  
   } else {  
     ## TODO: Implement HTML5 detection algorithm  
     require Whatpm::Charset::UniversalCharDet;  
     $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string  
         (substr ($$bytes_s, 0, 1024));  
     $charset ||= 'windows-1252';  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = $charset;  
     $self->{confident} = 0;  
   }  
410    
411    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
412      my $self = shift;      my (%opt) = @_;
413      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
414      ## TODO: if $charset is supported    };
415      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
416    
417      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
418        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
419      ## Step 1        };
420      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
421        $charset = 'utf-8';    ## HTML5 encoding sniffing algorithm
422      require Message::Charset::Info;
423      my $charset;
424      my $buffer;
425      my ($char_stream, $e_status);
426    
427      SNIFFING: {
428        ## NOTE: By setting |allow_fallback| option true when the
429        ## |get_decode_handle| method is invoked, we ignore what the HTML5
430        ## spec requires, i.e. unsupported encoding should be ignored.
431          ## TODO: We should not do this unless the parser is invoked
432          ## in the conformance checking mode, in which this behavior
433          ## would be useful.
434    
435        ## Step 1
436        if (defined $charset_name) {
437          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
438              ## TODO: Is this ok?  Transfer protocol's parameter should be
439              ## interpreted in its semantics?
440    
441          ($char_stream, $e_status) = $charset->get_decode_handle
442              ($byte_stream, allow_error_reporting => 1,
443               allow_fallback => 1);
444          if ($char_stream) {
445            $self->{confident} = 1;
446            last SNIFFING;
447          } else {
448            !!!parse-error (type => 'charset:not supported',
449                            layer => 'encode',
450                            line => 1, column => 1,
451                            value => $charset_name,
452                            level => $self->{level}->{uncertain});
453          }
454      }      }
455    
456      ## Step 2      ## Step 2
457      if (defined $self->{input_encoding} and      my $byte_buffer = '';
458          $self->{input_encoding} eq $charset) {      for (1..1024) {
459          my $char = $byte_stream->getc;
460          last unless defined $char;
461          $byte_buffer .= $char;
462        } ## TODO: timeout
463    
464        ## Step 3
465        if ($byte_buffer =~ /^\xFE\xFF/) {
466          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
467          ($char_stream, $e_status) = $charset->get_decode_handle
468              ($byte_stream, allow_error_reporting => 1,
469               allow_fallback => 1, byte_buffer => \$byte_buffer);
470        $self->{confident} = 1;        $self->{confident} = 1;
471        return;        last SNIFFING;
472        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
473          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
474          ($char_stream, $e_status) = $charset->get_decode_handle
475              ($byte_stream, allow_error_reporting => 1,
476               allow_fallback => 1, byte_buffer => \$byte_buffer);
477          $self->{confident} = 1;
478          last SNIFFING;
479        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
480          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
481          ($char_stream, $e_status) = $charset->get_decode_handle
482              ($byte_stream, allow_error_reporting => 1,
483               allow_fallback => 1, byte_buffer => \$byte_buffer);
484          $self->{confident} = 1;
485          last SNIFFING;
486      }      }
487    
488      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
489          ':'.$charset, level => 'w');      ## TODO: <meta charset>
490    
491      ## Step 3      ## Step 5
492      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
493    
494      ## Step 4      ## Step 6
495      throw Whatpm::HTML::RestartParser (charset => $charset);      require Whatpm::Charset::UniversalCharDet;
496        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
497            ($byte_buffer);
498        if (defined $charset_name) {
499          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
500    
501          require Whatpm::Charset::DecodeHandle;
502          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
503              ($byte_stream);
504          ($char_stream, $e_status) = $charset->get_decode_handle
505              ($buffer, allow_error_reporting => 1,
506               allow_fallback => 1, byte_buffer => \$byte_buffer);
507          if ($char_stream) {
508            $buffer->{buffer} = $byte_buffer;
509            !!!parse-error (type => 'sniffing:chardet',
510                            text => $charset_name,
511                            level => $self->{level}->{info},
512                            layer => 'encode',
513                            line => 1, column => 1);
514            $self->{confident} = 0;
515            last SNIFFING;
516          }
517        }
518    
519        ## Step 7: default
520        ## TODO: Make this configurable.
521        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
522            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
523            ## detectable in the step 6.
524        require Whatpm::Charset::DecodeHandle;
525        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
526            ($byte_stream);
527        ($char_stream, $e_status)
528            = $charset->get_decode_handle ($buffer,
529                                           allow_error_reporting => 1,
530                                           allow_fallback => 1,
531                                           byte_buffer => \$byte_buffer);
532        $buffer->{buffer} = $byte_buffer;
533        !!!parse-error (type => 'sniffing:default',
534                        text => 'windows-1252',
535                        level => $self->{level}->{info},
536                        line => 1, column => 1,
537                        layer => 'encode');
538        $self->{confident} = 0;
539      } # SNIFFING
540    
541      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
542        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
543        !!!parse-error (type => 'chardecode:fallback',
544                        #text => $self->{input_encoding},
545                        level => $self->{level}->{uncertain},
546                        line => 1, column => 1,
547                        layer => 'encode');
548      } elsif (not ($e_status &
549                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
550        $self->{input_encoding} = $charset->get_iana_name;
551        !!!parse-error (type => 'chardecode:no error',
552                        text => $self->{input_encoding},
553                        level => $self->{level}->{uncertain},
554                        line => 1, column => 1,
555                        layer => 'encode');
556      } else {
557        $self->{input_encoding} = $charset->get_iana_name;
558      }
559    
560      $self->{change_encoding} = sub {
561        my $self = shift;
562        $charset_name = shift;
563        my $token = shift;
564    
565        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
566        ($char_stream, $e_status) = $charset->get_decode_handle
567            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
568             byte_buffer => \ $buffer->{buffer});
569        
570        if ($char_stream) { # if supported
571          ## "Change the encoding" algorithm:
572    
573          ## Step 1    
574          if ($charset->{category} &
575              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
576            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
577            ($char_stream, $e_status) = $charset->get_decode_handle
578                ($byte_stream,
579                 byte_buffer => \ $buffer->{buffer});
580          }
581          $charset_name = $charset->get_iana_name;
582          
583          ## Step 2
584          if (defined $self->{input_encoding} and
585              $self->{input_encoding} eq $charset_name) {
586            !!!parse-error (type => 'charset label:matching',
587                            text => $charset_name,
588                            level => $self->{level}->{info});
589            $self->{confident} = 1;
590            return;
591          }
592    
593          !!!parse-error (type => 'charset label detected',
594                          text => $self->{input_encoding},
595                          value => $charset_name,
596                          level => $self->{level}->{warn},
597                          token => $token);
598          
599          ## Step 3
600          # if (can) {
601            ## change the encoding on the fly.
602            #$self->{confident} = 1;
603            #return;
604          # }
605          
606          ## Step 4
607          throw Whatpm::HTML::RestartParser ();
608        }
609    }; # $self->{change_encoding}    }; # $self->{change_encoding}
610    
611    my @args = @_; shift @args; # $s    my $char_onerror = sub {
612        my (undef, $type, %opt) = @_;
613        !!!parse-error (layer => 'encode',
614                        line => $self->{line}, column => $self->{column} + 1,
615                        %opt, type => $type);
616        if ($opt{octets}) {
617          ${$opt{octets}} = "\x{FFFD}"; # relacement character
618        }
619      };
620    
621      my $wrapped_char_stream = $get_wrapper->($char_stream);
622      $wrapped_char_stream->onerror ($char_onerror);
623    
624      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
625    my $return;    my $return;
626    try {    try {
627      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
628    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
629      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
630      $s = \ (Encode::decode ($charset, $$bytes_s));      
631      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
632          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
633          !!!parse-error (type => 'chardecode:fallback',
634                          level => $self->{level}->{uncertain},
635                          #text => $self->{input_encoding},
636                          line => 1, column => 1,
637                          layer => 'encode');
638        } elsif (not ($e_status &
639                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
640          $self->{input_encoding} = $charset->get_iana_name;
641          !!!parse-error (type => 'chardecode:no error',
642                          text => $self->{input_encoding},
643                          level => $self->{level}->{uncertain},
644                          line => 1, column => 1,
645                          layer => 'encode');
646        } else {
647          $self->{input_encoding} = $charset->get_iana_name;
648        }
649      $self->{confident} = 1;      $self->{confident} = 1;
650      $return = $self->parse_char_string ($s, @args);  
651        $wrapped_char_stream = $get_wrapper->($char_stream);
652        $wrapped_char_stream->onerror ($char_onerror);
653    
654        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
655    };    };
656    return $return;    return $return;
657  } # parse_byte_string  } # parse_byte_stream
658    
659  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
660  ## and the HTML layer MUST ignore it.  However, we does strip BOM in  ## and the HTML layer MUST ignore it.  However, we does strip BOM in
# Line 162  sub parse_byte_string ($$$$;$) { Line 665  sub parse_byte_string ($$$$;$) {
665  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
666  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
667    
668  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
669      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
670      my $self = shift;
671      my $s = ref $_[0] ? $_[0] : \($_[0]);
672      require Whatpm::Charset::DecodeHandle;
673      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
674      return $self->parse_char_stream ($input, @_[1..$#_]);
675    } # parse_char_string
676    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
677    
678  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
679    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
680    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
681    $self->{document} = $_[1];    $self->{document} = $_[1];
682    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
683    
# Line 175  sub parse_string ($$$;$) { Line 686  sub parse_string ($$$;$) {
686    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
687    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
688        if defined $self->{input_encoding};        if defined $self->{input_encoding};
689    ## TODO: |{input_encoding}| is needless?
690    
691    my $i = 0;    $self->{line_prev} = $self->{line} = 1;
692    my $line = 1;    $self->{column_prev} = -1;
693    my $column = 0;    $self->{column} = 0;
694    $self->{set_next_char} = sub {    $self->{set_nc} = sub {
695      my $self = shift;      my $self = shift;
696    
697      pop @{$self->{prev_char}};      my $char = '';
698      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
699          $char = $self->{next_nc};
700          delete $self->{next_nc};
701          $self->{nc} = ord $char;
702        } else {
703          $self->{char_buffer} = '';
704          $self->{char_buffer_pos} = 0;
705    
706      $self->{next_char} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
707      $self->{next_char} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
708      $column++;        if ($count) {
709            $self->{line_prev} = $self->{line};
710            $self->{column_prev} = $self->{column};
711            $self->{column}++;
712            $self->{nc}
713                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
714            return;
715          }
716    
717          if ($input->read ($char, 1)) {
718            $self->{nc} = ord $char;
719          } else {
720            $self->{nc} = -1;
721            return;
722          }
723        }
724    
725        ($self->{line_prev}, $self->{column_prev})
726            = ($self->{line}, $self->{column});
727        $self->{column}++;
728            
729      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
730        $line++;        !!!cp ('j1');
731        $column = 0;        $self->{line}++;
732      } elsif ($self->{next_char} == 0x000D) { # CR        $self->{column} = 0;
733        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
734        $self->{next_char} = 0x000A; # LF # MUST        !!!cp ('j2');
735        $line++;  ## TODO: support for abort/streaming
736        $column = 0;        my $next = '';
737      } elsif ($self->{next_char} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
738        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
739      } elsif ($self->{next_char} == 0x0000) { # NULL        }
740          $self->{nc} = 0x000A; # LF # MUST
741          $self->{line}++;
742          $self->{column} = 0;
743        } elsif ($self->{nc} == 0x0000) { # NULL
744          !!!cp ('j4');
745        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
746        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
747      }      }
748    };    };
749    $self->{prev_char} = [-1, -1, -1];  
750    $self->{next_char} = -1;    $self->{read_until} = sub {
751        #my ($scalar, $specials_range, $offset) = @_;
752        return 0 if defined $self->{next_nc};
753    
754        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
755        my $offset = $_[2] || 0;
756    
757        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
758          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
759          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
760            substr ($_[0], $offset)
761                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
762            my $count = $+[0] - $-[0];
763            if ($count) {
764              $self->{column} += $count;
765              $self->{char_buffer_pos} += $count;
766              $self->{line_prev} = $self->{line};
767              $self->{column_prev} = $self->{column} - 1;
768              $self->{nc} = -1;
769            }
770            return $count;
771          } else {
772            return 0;
773          }
774        } else {
775          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
776          if ($count) {
777            $self->{column} += $count;
778            $self->{line_prev} = $self->{line};
779            $self->{column_prev} = $self->{column} - 1;
780            $self->{nc} = -1;
781          }
782          return $count;
783        }
784      }; # $self->{read_until}
785    
786    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
787      my (%opt) = @_;      my (%opt) = @_;
788      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
789        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
790        warn "Parse error ($opt{type}) at line $line column $column\n";
791    };    };
792    $self->{parse_error} = sub {    $self->{parse_error} = sub {
793      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
794    };    };
795    
796      my $char_onerror = sub {
797        my (undef, $type, %opt) = @_;
798        !!!parse-error (layer => 'encode',
799                        line => $self->{line}, column => $self->{column} + 1,
800                        %opt, type => $type);
801      }; # $char_onerror
802    
803      if ($_[3]) {
804        $input = $_[3]->($input);
805        $input->onerror ($char_onerror);
806      } else {
807        $input->onerror ($char_onerror) unless defined $input->onerror;
808      }
809    
810    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
811    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
812    $self->_construct_tree;    $self->_construct_tree;
813    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
814    
815      delete $self->{parse_error}; # remove loop
816    
817    return $self->{document};    return $self->{document};
818  } # parse_string  } # parse_char_stream
819    
820  sub new ($) {  sub new ($) {
821    my $class = shift;    my $class = shift;
822    my $self = bless {}, $class;    my $self = bless {
823    $self->{set_next_char} = sub {      level => {must => 'm',
824      $self->{next_char} = -1;                should => 's',
825                  warn => 'w',
826                  info => 'i',
827                  uncertain => 'u'},
828      }, $class;
829      $self->{set_nc} = sub {
830        $self->{nc} = -1;
831    };    };
832    $self->{parse_error} = sub {    $self->{parse_error} = sub {
833      #      #
# Line 254  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 854  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
854  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
855    
856  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
857  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
858  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
859  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
860  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 265  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 865  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
865  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
866  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
867  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
868  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
869  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
870  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
871  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 287  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 887  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
887  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
888  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
889  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
890    sub SELF_CLOSING_START_TAG_STATE () { 34 }
891    sub CDATA_SECTION_STATE () { 35 }
892    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
893    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
894    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
895    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
896    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
897    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
898    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
899    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
900    ## NOTE: "Entity data state", "entity in attribute value state", and
901    ## "consume a character reference" algorithm are jointly implemented
902    ## using the following six states:
903    sub ENTITY_STATE () { 44 }
904    sub ENTITY_HASH_STATE () { 45 }
905    sub NCR_NUM_STATE () { 46 }
906    sub HEXREF_X_STATE () { 47 }
907    sub HEXREF_HEX_STATE () { 48 }
908    sub ENTITY_NAME_STATE () { 49 }
909    sub PCDATA_STATE () { 50 } # "data state" in the spec
910    
911  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
912  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 304  sub ROW_IMS ()        { 0b10000000 } Line 924  sub ROW_IMS ()        { 0b10000000 }
924  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
925  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
926  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
927    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
928        ## NOTE: "in foreign content" insertion mode is special; it is combined
929        ## with the secondary insertion mode.  In this parser, they are stored
930        ## together in the bit-or'ed form.
931    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
932        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
933        ## combined with the original insertion mode.  In thie parser,
934        ## they are stored together in the bit-or'ed form.
935    
936  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
937    
# Line 335  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 963  sub IN_COLUMN_GROUP_IM () { 0b10 }
963  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
964    my $self = shift;    my $self = shift;
965    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
966      #$self->{s_kwd}; # state keyword - initialized when used
967      #$self->{entity__value}; # initialized when used
968      #$self->{entity__match}; # initialized when used
969    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
970    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
971    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
972    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
973    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
974    $self->{char} = [];    delete $self->{self_closing};
975    # $self->{next_char}    $self->{char_buffer} = '';
976      $self->{char_buffer_pos} = 0;
977      $self->{nc} = -1; # next input character
978      #$self->{next_nc}
979    !!!next-input-character;    !!!next-input-character;
980    $self->{token} = [];    $self->{token} = [];
981    # $self->{escape}    # $self->{escape}
# Line 352  sub _initialize_tokenizer ($) { Line 986  sub _initialize_tokenizer ($) {
986  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
987  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
988  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
989  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
990  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
991  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
992  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
993  ##        ->{name}  ##        ->{name}
994  ##        ->{value}  ##        ->{value}
995  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
996  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
997    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
998    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
999    ##     while the token is pushed back to the stack.
1000    
1001  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
1002    
# Line 369  sub _initialize_tokenizer ($) { Line 1006  sub _initialize_tokenizer ($) {
1006  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
1007  ## and removed from the list.  ## and removed from the list.
1008    
1009  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1010  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
1011  ## contains some requirements that are not detected by the  
1012  ## parsing algorithm:  my $is_space = {
1013  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1014  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1015  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1016  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1017  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1018  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1019    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1020    
1021  sub _get_next_token ($) {  sub _get_next_token ($) {
1022    my $self = shift;    my $self = shift;
1023    
1024      if ($self->{self_closing}) {
1025        !!!parse-error (type => 'nestc', token => $self->{ct});
1026        ## NOTE: The |self_closing| flag is only set by start tag token.
1027        ## In addition, when a start tag token is emitted, it is always set to
1028        ## |ct|.
1029        delete $self->{self_closing};
1030      }
1031    
1032    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1033        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1034      return shift @{$self->{token}};      return shift @{$self->{token}};
1035    }    }
1036    
1037    A: {    A: {
1038      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1039        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1040    
1041          if ($self->{nc} == 0x0026) { # &
1042            !!!cp (0.1);
1043            ## NOTE: In the spec, the tokenizer is switched to the
1044            ## "entity data state".  In this implementation, the tokenizer
1045            ## is switched to the |ENTITY_STATE|, which is an implementation
1046            ## of the "consume a character reference" algorithm.
1047            $self->{entity_add} = -1;
1048            $self->{prev_state} = DATA_STATE;
1049            $self->{state} = ENTITY_STATE;
1050            !!!next-input-character;
1051            redo A;
1052          } elsif ($self->{nc} == 0x003C) { # <
1053            !!!cp (0.2);
1054            $self->{state} = TAG_OPEN_STATE;
1055            !!!next-input-character;
1056            redo A;
1057          } elsif ($self->{nc} == -1) {
1058            !!!cp (0.3);
1059            !!!emit ({type => END_OF_FILE_TOKEN,
1060                      line => $self->{line}, column => $self->{column}});
1061            last A; ## TODO: ok?
1062          } else {
1063            !!!cp (0.4);
1064            #
1065          }
1066    
1067          # Anything else
1068          my $token = {type => CHARACTER_TOKEN,
1069                       data => chr $self->{nc},
1070                       line => $self->{line}, column => $self->{column},
1071                      };
1072          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1073    
1074          ## Stay in the state.
1075          !!!next-input-character;
1076          !!!emit ($token);
1077          redo A;
1078        } elsif ($self->{state} == DATA_STATE) {
1079          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1080          if ($self->{nc} == 0x0026) { # &
1081            $self->{s_kwd} = '';
1082          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1083              not $self->{escape}) {              not $self->{escape}) {
1084            !!!cp (1);            !!!cp (1);
1085            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1086              ## "entity data state".  In this implementation, the tokenizer
1087              ## is switched to the |ENTITY_STATE|, which is an implementation
1088              ## of the "consume a character reference" algorithm.
1089              $self->{entity_add} = -1;
1090              $self->{prev_state} = DATA_STATE;
1091              $self->{state} = ENTITY_STATE;
1092            !!!next-input-character;            !!!next-input-character;
1093            redo A;            redo A;
1094          } else {          } else {
1095            !!!cp (2);            !!!cp (2);
1096            #            #
1097          }          }
1098        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1099          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1100            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1101              if ($self->{prev_char}->[0] == 0x002D and # -            
1102                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1103                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1104                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1105                $self->{escape} = 1;              $self->{s_kwd} = '--';
1106              } else {              #
1107                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1108              }              !!!cp (4);
1109                $self->{s_kwd} = '--';
1110                #
1111            } else {            } else {
1112              !!!cp (5);              !!!cp (5);
1113                #
1114            }            }
1115          }          }
1116                    
1117          #          #
1118        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1119            if (length $self->{s_kwd}) {
1120              !!!cp (5.1);
1121              $self->{s_kwd} .= '!';
1122              #
1123            } else {
1124              !!!cp (5.2);
1125              #$self->{s_kwd} = '';
1126              #
1127            }
1128            #
1129          } elsif ($self->{nc} == 0x003C) { # <
1130          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1131              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1132               not $self->{escape})) {               not $self->{escape})) {
# Line 430  sub _get_next_token ($) { Line 1136  sub _get_next_token ($) {
1136            redo A;            redo A;
1137          } else {          } else {
1138            !!!cp (7);            !!!cp (7);
1139              $self->{s_kwd} = '';
1140            #            #
1141          }          }
1142        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1143          if ($self->{escape} and          if ($self->{escape} and
1144              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1145            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1146              !!!cp (8);              !!!cp (8);
1147              delete $self->{escape};              delete $self->{escape};
1148            } else {            } else {
# Line 446  sub _get_next_token ($) { Line 1152  sub _get_next_token ($) {
1152            !!!cp (10);            !!!cp (10);
1153          }          }
1154                    
1155            $self->{s_kwd} = '';
1156          #          #
1157        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1158          !!!cp (11);          !!!cp (11);
1159          !!!emit ({type => END_OF_FILE_TOKEN});          $self->{s_kwd} = '';
1160            !!!emit ({type => END_OF_FILE_TOKEN,
1161                      line => $self->{line}, column => $self->{column}});
1162          last A; ## TODO: ok?          last A; ## TODO: ok?
1163        } else {        } else {
1164          !!!cp (12);          !!!cp (12);
1165            $self->{s_kwd} = '';
1166            #
1167        }        }
1168    
1169        # Anything else        # Anything else
1170        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1171                     data => chr $self->{next_char}};                     data => chr $self->{nc},
1172        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1173        !!!next-input-character;                    };
1174          if ($self->{read_until}->($token->{data}, q[-!<>&],
1175        !!!emit ($token);                                  length $token->{data})) {
1176            $self->{s_kwd} = '';
1177        redo A;        }
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
1178    
1179        unless (defined $token) {        ## Stay in the data state.
1180          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1181          !!!cp (13);          !!!cp (13);
1182          !!!emit ({type => CHARACTER_TOKEN, data => '&'});          $self->{state} = PCDATA_STATE;
1183        } else {        } else {
1184          !!!cp (14);          !!!cp (14);
1185          !!!emit ($token);          ## Stay in the state.
1186        }        }
1187          !!!next-input-character;
1188          !!!emit ($token);
1189        redo A;        redo A;
1190      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1191        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1192          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1193            !!!cp (15);            !!!cp (15);
1194            !!!next-input-character;            !!!next-input-character;
1195            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1196            redo A;            redo A;
1197            } elsif ($self->{nc} == 0x0021) { # !
1198              !!!cp (15.1);
1199              $self->{s_kwd} = '<' unless $self->{escape};
1200              #
1201          } else {          } else {
1202            !!!cp (16);            !!!cp (16);
1203            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<'});  
   
           redo A;  
1204          }          }
1205    
1206            ## reconsume
1207            $self->{state} = DATA_STATE;
1208            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1209                      line => $self->{line_prev},
1210                      column => $self->{column_prev},
1211                     });
1212            redo A;
1213        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1214          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1215            !!!cp (17);            !!!cp (17);
1216            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1217            !!!next-input-character;            !!!next-input-character;
1218            redo A;            redo A;
1219          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1220            !!!cp (18);            !!!cp (18);
1221            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1222            !!!next-input-character;            !!!next-input-character;
1223            redo A;            redo A;
1224          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1225                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1226            !!!cp (19);            !!!cp (19);
1227            $self->{current_token}            $self->{ct}
1228              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1229                 tag_name => chr ($self->{next_char} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
1230                   line => $self->{line_prev},
1231                   column => $self->{column_prev}};
1232            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1233            !!!next-input-character;            !!!next-input-character;
1234            redo A;            redo A;
1235          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1236                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1237            !!!cp (20);            !!!cp (20);
1238            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1239                              tag_name => chr ($self->{next_char})};                                      tag_name => chr ($self->{nc}),
1240                                        line => $self->{line_prev},
1241                                        column => $self->{column_prev}};
1242            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1243            !!!next-input-character;            !!!next-input-character;
1244            redo A;            redo A;
1245          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1246            !!!cp (21);            !!!cp (21);
1247            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag',
1248                              line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1251            !!!next-input-character;            !!!next-input-character;
1252    
1253            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1254                        line => $self->{line_prev},
1255                        column => $self->{column_prev},
1256                       });
1257    
1258            redo A;            redo A;
1259          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1260            !!!cp (22);            !!!cp (22);
1261            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio',
1262                              line => $self->{line_prev},
1263                              column => $self->{column_prev});
1264            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1265            ## $self->{next_char} is intentionally left as is            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1266                                        line => $self->{line_prev},
1267                                        column => $self->{column_prev},
1268                                       };
1269              ## $self->{nc} is intentionally left as is
1270            redo A;            redo A;
1271          } else {          } else {
1272            !!!cp (23);            !!!cp (23);
1273            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago',
1274                              line => $self->{line_prev},
1275                              column => $self->{column_prev});
1276            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1277            ## reconsume            ## reconsume
1278    
1279            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1280                        line => $self->{line_prev},
1281                        column => $self->{column_prev},
1282                       });
1283    
1284            redo A;            redo A;
1285          }          }
# Line 553  sub _get_next_token ($) { Line 1287  sub _get_next_token ($) {
1287          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1288        }        }
1289      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1290        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1291          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_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;  
1292    
1293                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1294            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1295                redo A;          if (defined $self->{last_stag_name}) {
1296              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1297            }            $self->{s_kwd} = '';
1298            push @next_char, $self->{next_char};            ## Reconsume.
1299                    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...  
           }  
1300          } else {          } else {
1301            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1302              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1303            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1304            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1305            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## Reconsume.
1306              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1307                        line => $l, column => $c,
1308                       });
1309            redo A;            redo A;
1310          }          }
1311        }        }
1312          
1313        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1314            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1315          !!!cp (29);          !!!cp (29);
1316          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct}
1317                            tag_name => chr ($self->{next_char} + 0x0020)};              = {type => END_TAG_TOKEN,
1318                   tag_name => chr ($self->{nc} + 0x0020),
1319                   line => $l, column => $c};
1320          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1321          !!!next-input-character;          !!!next-input-character;
1322          redo A;          redo A;
1323        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1324                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1325          !!!cp (30);          !!!cp (30);
1326          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1327                            tag_name => chr ($self->{next_char})};                                    tag_name => chr ($self->{nc}),
1328                                      line => $l, column => $c};
1329          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1330          !!!next-input-character;          !!!next-input-character;
1331          redo A;          redo A;
1332        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1333          !!!cp (31);          !!!cp (31);
1334          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag',
1335                            line => $self->{line_prev}, ## "<" in "</>"
1336                            column => $self->{column_prev} - 1);
1337          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1338          !!!next-input-character;          !!!next-input-character;
1339          redo A;          redo A;
1340        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1341          !!!cp (32);          !!!cp (32);
1342          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1343          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1344          # reconsume          # reconsume
1345    
1346          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1347                      line => $l, column => $c,
1348                     });
1349    
1350          redo A;          redo A;
1351        } else {        } else {
1352          !!!cp (33);          !!!cp (33);
1353          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1354          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1355          ## $self->{next_char} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1356          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1357                                      column => $self->{column_prev} - 1,
1358                                     };
1359            ## NOTE: $self->{nc} is intentionally left as is.
1360            ## Although the "anything else" case of the spec not explicitly
1361            ## states that the next input character is to be reconsumed,
1362            ## it will be included to the |data| of the comment token
1363            ## generated from the bogus end tag, as defined in the
1364            ## "bogus comment state" entry.
1365            redo A;
1366          }
1367        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1368          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1369          if (length $ch) {
1370            my $CH = $ch;
1371            $ch =~ tr/a-z/A-Z/;
1372            my $nch = chr $self->{nc};
1373            if ($nch eq $ch or $nch eq $CH) {
1374              !!!cp (24);
1375              ## Stay in the state.
1376              $self->{s_kwd} .= $nch;
1377              !!!next-input-character;
1378              redo A;
1379            } else {
1380              !!!cp (25);
1381              $self->{state} = DATA_STATE;
1382              ## Reconsume.
1383              !!!emit ({type => CHARACTER_TOKEN,
1384                        data => '</' . $self->{s_kwd},
1385                        line => $self->{line_prev},
1386                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1387                       });
1388              redo A;
1389            }
1390          } else { # after "<{tag-name}"
1391            unless ($is_space->{$self->{nc}} or
1392                    {
1393                     0x003E => 1, # >
1394                     0x002F => 1, # /
1395                     -1 => 1, # EOF
1396                    }->{$self->{nc}}) {
1397              !!!cp (26);
1398              ## Reconsume.
1399              $self->{state} = DATA_STATE;
1400              !!!emit ({type => CHARACTER_TOKEN,
1401                        data => '</' . $self->{s_kwd},
1402                        line => $self->{line_prev},
1403                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1404                       });
1405              redo A;
1406            } else {
1407              !!!cp (27);
1408              $self->{ct}
1409                  = {type => END_TAG_TOKEN,
1410                     tag_name => $self->{last_stag_name},
1411                     line => $self->{line_prev},
1412                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1413              $self->{state} = TAG_NAME_STATE;
1414              ## Reconsume.
1415              redo A;
1416            }
1417        }        }
1418      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1419        if ($self->{next_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  
1420          !!!cp (34);          !!!cp (34);
1421          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1422          !!!next-input-character;          !!!next-input-character;
1423          redo A;          redo A;
1424        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1425          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1426            !!!cp (35);            !!!cp (35);
1427            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1428                = 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) {  
1429            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1430            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1431            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1432            #  !!! cp (36);            #  !!! cp (36);
1433            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 672  sub _get_next_token ($) { Line 1435  sub _get_next_token ($) {
1435              !!!cp (37);              !!!cp (37);
1436            #}            #}
1437          } else {          } else {
1438            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1439          }          }
1440          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1441          !!!next-input-character;          !!!next-input-character;
1442    
1443          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1444    
1445          redo A;          redo A;
1446        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1447                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1448          !!!cp (38);          !!!cp (38);
1449          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1450            # start tag or end tag            # start tag or end tag
1451          ## Stay in this state          ## Stay in this state
1452          !!!next-input-character;          !!!next-input-character;
1453          redo A;          redo A;
1454        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1455          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1456          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1457            !!!cp (39);            !!!cp (39);
1458            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1459                = 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) {  
1460            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1461            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1462            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1463            #  !!! cp (40);            #  !!! cp (40);
1464            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 705  sub _get_next_token ($) { Line 1466  sub _get_next_token ($) {
1466              !!!cp (41);              !!!cp (41);
1467            #}            #}
1468          } else {          } else {
1469            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1470          }          }
1471          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1472          # reconsume          # reconsume
1473    
1474          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1475    
1476          redo A;          redo A;
1477        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1478            !!!cp (42);
1479            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1480          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_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  
1481          redo A;          redo A;
1482        } else {        } else {
1483          !!!cp (44);          !!!cp (44);
1484          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1485            # start tag or end tag            # start tag or end tag
1486          ## Stay in the state          ## Stay in the state
1487          !!!next-input-character;          !!!next-input-character;
1488          redo A;          redo A;
1489        }        }
1490      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1491        if ($self->{next_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  
1492          !!!cp (45);          !!!cp (45);
1493          ## Stay in the state          ## Stay in the state
1494          !!!next-input-character;          !!!next-input-character;
1495          redo A;          redo A;
1496        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1497          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1498            !!!cp (46);            !!!cp (46);
1499            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1500                = 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) {  
1501            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1502            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1503              !!!cp (47);              !!!cp (47);
1504              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1505            } else {            } else {
1506              !!!cp (48);              !!!cp (48);
1507            }            }
1508          } else {          } else {
1509            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1510          }          }
1511          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1512          !!!next-input-character;          !!!next-input-character;
1513    
1514          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1515    
1516          redo A;          redo A;
1517        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1518                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1519          !!!cp (49);          !!!cp (49);
1520          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1521                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1522                   value => '',
1523                   line => $self->{line}, column => $self->{column}};
1524          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1525          !!!next-input-character;          !!!next-input-character;
1526          redo A;          redo A;
1527        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1528            !!!cp (50);
1529            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1530          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_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  
1531          redo A;          redo A;
1532        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1533          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1534          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1535            !!!cp (52);            !!!cp (52);
1536            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1537                = 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) {  
1538            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1539            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1540              !!!cp (53);              !!!cp (53);
1541              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1542            } else {            } else {
1543              !!!cp (54);              !!!cp (54);
1544            }            }
1545          } else {          } else {
1546            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1547          }          }
1548          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1549          # reconsume          # reconsume
1550    
1551          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1552    
1553          redo A;          redo A;
1554        } else {        } else {
# Line 821  sub _get_next_token ($) { Line 1556  sub _get_next_token ($) {
1556               0x0022 => 1, # "               0x0022 => 1, # "
1557               0x0027 => 1, # '               0x0027 => 1, # '
1558               0x003D => 1, # =               0x003D => 1, # =
1559              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1560            !!!cp (55);            !!!cp (55);
1561            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1562          } else {          } else {
1563            !!!cp (56);            !!!cp (56);
1564          }          }
1565          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{ca}
1566                                value => ''};              = {name => chr ($self->{nc}),
1567                   value => '',
1568                   line => $self->{line}, column => $self->{column}};
1569          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1570          !!!next-input-character;          !!!next-input-character;
1571          redo A;          redo A;
1572        }        }
1573      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1574        my $before_leave = sub {        my $before_leave = sub {
1575          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1576              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1577            !!!cp (57);            !!!cp (57);
1578            !!!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});
1579            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1580          } else {          } else {
1581            !!!cp (58);            !!!cp (58);
1582            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1583              = $self->{current_attribute};              = $self->{ca};
1584          }          }
1585        }; # $before_leave        }; # $before_leave
1586    
1587        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  
1588          !!!cp (59);          !!!cp (59);
1589          $before_leave->();          $before_leave->();
1590          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1591          !!!next-input-character;          !!!next-input-character;
1592          redo A;          redo A;
1593        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1594          !!!cp (60);          !!!cp (60);
1595          $before_leave->();          $before_leave->();
1596          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1597          !!!next-input-character;          !!!next-input-character;
1598          redo A;          redo A;
1599        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1600          $before_leave->();          $before_leave->();
1601          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1602            !!!cp (61);            !!!cp (61);
1603            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1604                = 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) {  
1605            !!!cp (62);            !!!cp (62);
1606            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1607            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1608              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1609            }            }
1610          } else {          } else {
1611            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1612          }          }
1613          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1614          !!!next-input-character;          !!!next-input-character;
1615    
1616          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1617    
1618          redo A;          redo A;
1619        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1620                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1621          !!!cp (63);          !!!cp (63);
1622          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1623          ## Stay in the state          ## Stay in the state
1624          !!!next-input-character;          !!!next-input-character;
1625          redo A;          redo A;
1626        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1627            !!!cp (64);
1628          $before_leave->();          $before_leave->();
1629            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1630          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_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  
1631          redo A;          redo A;
1632        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1633          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1634          $before_leave->();          $before_leave->();
1635          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1636            !!!cp (66);            !!!cp (66);
1637            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1638                = 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) {  
1639            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1640            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1641              !!!cp (67);              !!!cp (67);
1642              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1643            } else {            } else {
# Line 926  sub _get_next_token ($) { Line 1645  sub _get_next_token ($) {
1645              !!!cp (68);              !!!cp (68);
1646            }            }
1647          } else {          } else {
1648            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1649          }          }
1650          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1651          # reconsume          # reconsume
1652    
1653          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1654    
1655          redo A;          redo A;
1656        } else {        } else {
1657          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1658              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1659            !!!cp (69);            !!!cp (69);
1660            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1661          } else {          } else {
1662            !!!cp (70);            !!!cp (70);
1663          }          }
1664          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1665          ## Stay in the state          ## Stay in the state
1666          !!!next-input-character;          !!!next-input-character;
1667          redo A;          redo A;
1668        }        }
1669      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1670        if ($self->{next_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  
1671          !!!cp (71);          !!!cp (71);
1672          ## Stay in the state          ## Stay in the state
1673          !!!next-input-character;          !!!next-input-character;
1674          redo A;          redo A;
1675        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1676          !!!cp (72);          !!!cp (72);
1677          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1678          !!!next-input-character;          !!!next-input-character;
1679          redo A;          redo A;
1680        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1681          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1682            !!!cp (73);            !!!cp (73);
1683            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1684                = 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) {  
1685            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1686            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1687              !!!cp (74);              !!!cp (74);
1688              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1689            } else {            } else {
# Line 978  sub _get_next_token ($) { Line 1691  sub _get_next_token ($) {
1691              !!!cp (75);              !!!cp (75);
1692            }            }
1693          } else {          } else {
1694            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1695          }          }
1696          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1697          !!!next-input-character;          !!!next-input-character;
1698    
1699          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1700    
1701          redo A;          redo A;
1702        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1703                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1704          !!!cp (76);          !!!cp (76);
1705          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1706                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1707                   value => '',
1708                   line => $self->{line}, column => $self->{column}};
1709          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1710          !!!next-input-character;          !!!next-input-character;
1711          redo A;          redo A;
1712        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1713            !!!cp (77);
1714            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1715          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_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  
1716          redo A;          redo A;
1717        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1718          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1719          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1720            !!!cp (79);            !!!cp (79);
1721            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1722                = 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) {  
1723            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1724            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1725              !!!cp (80);              !!!cp (80);
1726              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1727            } else {            } else {
# Line 1027  sub _get_next_token ($) { Line 1729  sub _get_next_token ($) {
1729              !!!cp (81);              !!!cp (81);
1730            }            }
1731          } else {          } else {
1732            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1733          }          }
1734          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1735          # reconsume          # reconsume
1736    
1737          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1738    
1739          redo A;          redo A;
1740        } else {        } else {
1741          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1742          $self->{current_attribute} = {name => chr ($self->{next_char}),              $self->{nc} == 0x0027) { # '
1743                                value => ''};            !!!cp (78);
1744              !!!parse-error (type => 'bad attribute name');
1745            } else {
1746              !!!cp (82);
1747            }
1748            $self->{ca}
1749                = {name => chr ($self->{nc}),
1750                   value => '',
1751                   line => $self->{line}, column => $self->{column}};
1752          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1753          !!!next-input-character;          !!!next-input-character;
1754          redo A;                  redo A;        
1755        }        }
1756      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1757        if ($self->{next_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        
1758          !!!cp (83);          !!!cp (83);
1759          ## Stay in the state          ## Stay in the state
1760          !!!next-input-character;          !!!next-input-character;
1761          redo A;          redo A;
1762        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1763          !!!cp (84);          !!!cp (84);
1764          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1765          !!!next-input-character;          !!!next-input-character;
1766          redo A;          redo A;
1767        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1768          !!!cp (85);          !!!cp (85);
1769          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1770          ## reconsume          ## reconsume
1771          redo A;          redo A;
1772        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1773          !!!cp (86);          !!!cp (86);
1774          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1775          !!!next-input-character;          !!!next-input-character;
1776          redo A;          redo A;
1777        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1778          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1779            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1780            !!!cp (87);            !!!cp (87);
1781            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1782                = 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) {  
1783            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1784            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1785              !!!cp (88);              !!!cp (88);
1786              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1787            } else {            } else {
# Line 1084  sub _get_next_token ($) { Line 1789  sub _get_next_token ($) {
1789              !!!cp (89);              !!!cp (89);
1790            }            }
1791          } else {          } else {
1792            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1793          }          }
1794          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1795          !!!next-input-character;          !!!next-input-character;
1796    
1797          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1798    
1799          redo A;          redo A;
1800        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1801          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1802          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1803            !!!cp (90);            !!!cp (90);
1804            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1805                = 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) {  
1806            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1807            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1808              !!!cp (91);              !!!cp (91);
1809              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1810            } else {            } else {
# Line 1109  sub _get_next_token ($) { Line 1812  sub _get_next_token ($) {
1812              !!!cp (92);              !!!cp (92);
1813            }            }
1814          } else {          } else {
1815            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1816          }          }
1817          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1818          ## reconsume          ## reconsume
1819    
1820          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1821    
1822          redo A;          redo A;
1823        } else {        } else {
1824          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1825            !!!cp (93);            !!!cp (93);
1826            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1827          } else {          } else {
1828            !!!cp (94);            !!!cp (94);
1829          }          }
1830          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1831          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1832          !!!next-input-character;          !!!next-input-character;
1833          redo A;          redo A;
1834        }        }
1835      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1836        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1837          !!!cp (95);          !!!cp (95);
1838          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1839          !!!next-input-character;          !!!next-input-character;
1840          redo A;          redo A;
1841        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1842          !!!cp (96);          !!!cp (96);
1843          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1844          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1845            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1846            ## implementation of the "consume a character reference" algorithm.
1847            $self->{prev_state} = $self->{state};
1848            $self->{entity_add} = 0x0022; # "
1849            $self->{state} = ENTITY_STATE;
1850          !!!next-input-character;          !!!next-input-character;
1851          redo A;          redo A;
1852        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1853          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1854          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1855            !!!cp (97);            !!!cp (97);
1856            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1857                = 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) {  
1858            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1859            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1860              !!!cp (98);              !!!cp (98);
1861              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1862            } else {            } else {
# Line 1158  sub _get_next_token ($) { Line 1864  sub _get_next_token ($) {
1864              !!!cp (99);              !!!cp (99);
1865            }            }
1866          } else {          } else {
1867            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1868          }          }
1869          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1870          ## reconsume          ## reconsume
1871    
1872          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1873    
1874          redo A;          redo A;
1875        } else {        } else {
1876          !!!cp (100);          !!!cp (100);
1877          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1878            $self->{read_until}->($self->{ca}->{value},
1879                                  q["&],
1880                                  length $self->{ca}->{value});
1881    
1882          ## Stay in the state          ## Stay in the state
1883          !!!next-input-character;          !!!next-input-character;
1884          redo A;          redo A;
1885        }        }
1886      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1887        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1888          !!!cp (101);          !!!cp (101);
1889          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1890          !!!next-input-character;          !!!next-input-character;
1891          redo A;          redo A;
1892        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1893          !!!cp (102);          !!!cp (102);
1894          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1895          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1896            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1897            ## implementation of the "consume a character reference" algorithm.
1898            $self->{entity_add} = 0x0027; # '
1899            $self->{prev_state} = $self->{state};
1900            $self->{state} = ENTITY_STATE;
1901          !!!next-input-character;          !!!next-input-character;
1902          redo A;          redo A;
1903        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1904          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1905          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1906            !!!cp (103);            !!!cp (103);
1907            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1908                = 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) {  
1909            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1910            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1911              !!!cp (104);              !!!cp (104);
1912              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1913            } else {            } else {
# Line 1202  sub _get_next_token ($) { Line 1915  sub _get_next_token ($) {
1915              !!!cp (105);              !!!cp (105);
1916            }            }
1917          } else {          } else {
1918            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1919          }          }
1920          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1921          ## reconsume          ## reconsume
1922    
1923          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1924    
1925          redo A;          redo A;
1926        } else {        } else {
1927          !!!cp (106);          !!!cp (106);
1928          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1929            $self->{read_until}->($self->{ca}->{value},
1930                                  q['&],
1931                                  length $self->{ca}->{value});
1932    
1933          ## Stay in the state          ## Stay in the state
1934          !!!next-input-character;          !!!next-input-character;
1935          redo A;          redo A;
1936        }        }
1937      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1938        if ($self->{next_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  
1939          !!!cp (107);          !!!cp (107);
1940          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1941          !!!next-input-character;          !!!next-input-character;
1942          redo A;          redo A;
1943        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1944          !!!cp (108);          !!!cp (108);
1945          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1946          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1947            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1948            ## implementation of the "consume a character reference" algorithm.
1949            $self->{entity_add} = -1;
1950            $self->{prev_state} = $self->{state};
1951            $self->{state} = ENTITY_STATE;
1952          !!!next-input-character;          !!!next-input-character;
1953          redo A;          redo A;
1954        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1955          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1956            !!!cp (109);            !!!cp (109);
1957            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1958                = 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) {  
1959            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1960            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1961              !!!cp (110);              !!!cp (110);
1962              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1963            } else {            } else {
# Line 1249  sub _get_next_token ($) { Line 1965  sub _get_next_token ($) {
1965              !!!cp (111);              !!!cp (111);
1966            }            }
1967          } else {          } else {
1968            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1969          }          }
1970          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1971          !!!next-input-character;          !!!next-input-character;
1972    
1973          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1974    
1975          redo A;          redo A;
1976        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1977          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1978          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1979            !!!cp (112);            !!!cp (112);
1980            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1981                = 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) {  
1982            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1983            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1984              !!!cp (113);              !!!cp (113);
1985              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1986            } else {            } else {
# Line 1274  sub _get_next_token ($) { Line 1988  sub _get_next_token ($) {
1988              !!!cp (114);              !!!cp (114);
1989            }            }
1990          } else {          } else {
1991            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1992          }          }
1993          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1994          ## reconsume          ## reconsume
1995    
1996          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1997    
1998          redo A;          redo A;
1999        } else {        } else {
# Line 1287  sub _get_next_token ($) { Line 2001  sub _get_next_token ($) {
2001               0x0022 => 1, # "               0x0022 => 1, # "
2002               0x0027 => 1, # '               0x0027 => 1, # '
2003               0x003D => 1, # =               0x003D => 1, # =
2004              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
2005            !!!cp (115);            !!!cp (115);
2006            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
2007          } else {          } else {
2008            !!!cp (116);            !!!cp (116);
2009          }          }
2010          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
2011            $self->{read_until}->($self->{ca}->{value},
2012                                  q["'=& >],
2013                                  length $self->{ca}->{value});
2014    
2015          ## Stay in the state          ## Stay in the state
2016          !!!next-input-character;          !!!next-input-character;
2017          redo A;          redo A;
2018        }        }
     } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {  
       my $token = $self->_tokenize_attempt_to_consume_an_entity  
           (1,  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '  
            -1);  
   
       unless (defined $token) {  
         !!!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;  
2019      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2020        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  
2021          !!!cp (118);          !!!cp (118);
2022          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2023          !!!next-input-character;          !!!next-input-character;
2024          redo A;          redo A;
2025        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2026          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2027            !!!cp (119);            !!!cp (119);
2028            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
2029                = 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) {  
2030            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2031            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2032              !!!cp (120);              !!!cp (120);
2033              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2034            } else {            } else {
# Line 1346  sub _get_next_token ($) { Line 2036  sub _get_next_token ($) {
2036              !!!cp (121);              !!!cp (121);
2037            }            }
2038          } else {          } else {
2039            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2040          }          }
2041          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2042          !!!next-input-character;          !!!next-input-character;
2043    
2044          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2045    
2046          redo A;          redo A;
2047        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2048            !!!cp (122);
2049            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2050          !!!next-input-character;          !!!next-input-character;
2051          if ($self->{next_char} == 0x003E and # >          redo A;
2052              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2053              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2054            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2055            !!!cp (122);            !!!cp (122.3);
2056            #            $self->{last_stag_name} = $self->{ct}->{tag_name};
2057            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2058              if ($self->{ct}->{attributes}) {
2059                !!!cp (122.1);
2060                !!!parse-error (type => 'end tag attribute');
2061              } else {
2062                ## NOTE: This state should never be reached.
2063                !!!cp (122.2);
2064              }
2065          } else {          } else {
2066            !!!cp (123);            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!parse-error (type => 'nestc');  
2067          }          }
2068          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2069          # next-input-character is already done          ## Reconsume.
2070            !!!emit ($self->{ct}); # start tag or end tag
2071          redo A;          redo A;
2072        } else {        } else {
2073          !!!cp (124);          !!!cp ('124.1');
2074          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2075          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2076          ## reconsume          ## reconsume
2077          redo A;          redo A;
2078        }        }
2079      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2080        ## (only happen if PCDATA state)        if ($self->{nc} == 0x003E) { # >
2081                  if ($self->{ct}->{type} == END_TAG_TOKEN) {
2082        my $token = {type => COMMENT_TOKEN, data => ''};            !!!cp ('124.2');
2083              !!!parse-error (type => 'nestc', token => $self->{ct});
2084        BC: {            ## TODO: Different type than slash in start tag
2085          if ($self->{next_char} == 0x003E) { # >            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2086            !!!cp (124);            if ($self->{ct}->{attributes}) {
2087            $self->{state} = DATA_STATE;              !!!cp ('124.4');
2088            !!!next-input-character;              !!!parse-error (type => 'end tag attribute');
2089              } else {
2090            !!!emit ($token);              !!!cp ('124.5');
2091              }
2092              ## TODO: Test |<title></title/>|
2093            } else {
2094              !!!cp ('124.3');
2095              $self->{self_closing} = 1;
2096            }
2097    
2098            redo A;          $self->{state} = DATA_STATE;
2099          } elsif ($self->{next_char} == -1) {          !!!next-input-character;
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2100    
2101            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2102    
2103            redo A;          redo A;
2104          } elsif ($self->{nc} == -1) {
2105            !!!parse-error (type => 'unclosed tag');
2106            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2107              !!!cp (124.7);
2108              $self->{last_stag_name} = $self->{ct}->{tag_name};
2109            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2110              if ($self->{ct}->{attributes}) {
2111                !!!cp (124.5);
2112                !!!parse-error (type => 'end tag attribute');
2113              } else {
2114                ## NOTE: This state should never be reached.
2115                !!!cp (124.6);
2116              }
2117          } else {          } else {
2118            !!!cp (126);            die "$0: $self->{ct}->{type}: Unknown token type";
           $token->{data} .= chr ($self->{next_char});  
           !!!next-input-character;  
           redo BC;  
2119          }          }
2120        } # BC          $self->{state} = DATA_STATE;
2121            ## Reconsume.
2122        die "$0: _get_next_token: unexpected case [BC]";          !!!emit ($self->{ct}); # start tag or end tag
2123      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {          redo A;
2124          } else {
2125            !!!cp ('124.4');
2126            !!!parse-error (type => 'nestc');
2127            ## TODO: This error type is wrong.
2128            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2129            ## Reconsume.
2130            redo A;
2131          }
2132        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2133        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2134    
2135        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2136        push @next_char, $self->{next_char};        ## consumes characters one-by-one basis.
2137                
2138        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2139            !!!cp (124);
2140            $self->{state} = DATA_STATE;
2141          !!!next-input-character;          !!!next-input-character;
2142          push @next_char, $self->{next_char};  
2143          if ($self->{next_char} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2144            !!!cp (127);          redo A;
2145            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};        } elsif ($self->{nc} == -1) {
2146            $self->{state} = COMMENT_START_STATE;          !!!cp (125);
2147            !!!next-input-character;          $self->{state} = DATA_STATE;
2148            redo A;          ## reconsume
2149          } else {  
2150            !!!cp (128);          !!!emit ($self->{ct}); # comment
2151          }          redo A;
2152        } elsif ($self->{next_char} == 0x0044 or # D        } else {
2153                 $self->{next_char} == 0x0064) { # d          !!!cp (126);
2154            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2155            $self->{read_until}->($self->{ct}->{data},
2156                                  q[>],
2157                                  length $self->{ct}->{data});
2158    
2159            ## Stay in the state.
2160          !!!next-input-character;          !!!next-input-character;
2161          push @next_char, $self->{next_char};          redo A;
2162          if ($self->{next_char} == 0x004F or # O        }
2163              $self->{next_char} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2164            !!!next-input-character;        ## (only happen if PCDATA state)
2165            push @next_char, $self->{next_char};        
2166            if ($self->{next_char} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2167                $self->{next_char} == 0x0063) { # c          !!!cp (133);
2168              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2169              push @next_char, $self->{next_char};          !!!next-input-character;
2170              if ($self->{next_char} == 0x0054 or # T          redo A;
2171                  $self->{next_char} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2172                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2173                push @next_char, $self->{next_char};          ## ASCII case-insensitive.
2174                if ($self->{next_char} == 0x0059 or # Y          !!!cp (130);
2175                    $self->{next_char} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2176                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2177                  push @next_char, $self->{next_char};          !!!next-input-character;
2178                  if ($self->{next_char} == 0x0050 or # P          redo A;
2179                      $self->{next_char} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2180                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2181                    push @next_char, $self->{next_char};                 $self->{nc} == 0x005B) { # [
2182                    if ($self->{next_char} == 0x0045 or # E          !!!cp (135.4);                
2183                        $self->{next_char} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2184                      !!!cp (129);          $self->{s_kwd} = '[';
2185                      ## TODO: What a stupid code this is!          !!!next-input-character;
2186                      $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);  
         }  
2187        } else {        } else {
2188          !!!cp (136);          !!!cp (136);
2189        }        }
2190    
2191        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2192        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2193        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2194          ## Reconsume.
2195        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2196          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2197                                    line => $self->{line_prev},
2198                                    column => $self->{column_prev} - 1,
2199                                   };
2200        redo A;        redo A;
2201              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2202        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2203        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2204            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2205                                      line => $self->{line_prev},
2206                                      column => $self->{column_prev} - 2,
2207                                     };
2208            $self->{state} = COMMENT_START_STATE;
2209            !!!next-input-character;
2210            redo A;
2211          } else {
2212            !!!cp (128);
2213            !!!parse-error (type => 'bogus comment',
2214                            line => $self->{line_prev},
2215                            column => $self->{column_prev} - 2);
2216            $self->{state} = BOGUS_COMMENT_STATE;
2217            ## Reconsume.
2218            $self->{ct} = {type => COMMENT_TOKEN,
2219                                      data => '-',
2220                                      line => $self->{line_prev},
2221                                      column => $self->{column_prev} - 2,
2222                                     };
2223            redo A;
2224          }
2225        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2226          ## ASCII case-insensitive.
2227          if ($self->{nc} == [
2228                undef,
2229                0x004F, # O
2230                0x0043, # C
2231                0x0054, # T
2232                0x0059, # Y
2233                0x0050, # P
2234              ]->[length $self->{s_kwd}] or
2235              $self->{nc} == [
2236                undef,
2237                0x006F, # o
2238                0x0063, # c
2239                0x0074, # t
2240                0x0079, # y
2241                0x0070, # p
2242              ]->[length $self->{s_kwd}]) {
2243            !!!cp (131);
2244            ## Stay in the state.
2245            $self->{s_kwd} .= chr $self->{nc};
2246            !!!next-input-character;
2247            redo A;
2248          } elsif ((length $self->{s_kwd}) == 6 and
2249                   ($self->{nc} == 0x0045 or # E
2250                    $self->{nc} == 0x0065)) { # e
2251            !!!cp (129);
2252            $self->{state} = DOCTYPE_STATE;
2253            $self->{ct} = {type => DOCTYPE_TOKEN,
2254                                      quirks => 1,
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 7,
2257                                     };
2258            !!!next-input-character;
2259            redo A;
2260          } else {
2261            !!!cp (132);        
2262            !!!parse-error (type => 'bogus comment',
2263                            line => $self->{line_prev},
2264                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2265            $self->{state} = BOGUS_COMMENT_STATE;
2266            ## Reconsume.
2267            $self->{ct} = {type => COMMENT_TOKEN,
2268                                      data => $self->{s_kwd},
2269                                      line => $self->{line_prev},
2270                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2271                                     };
2272            redo A;
2273          }
2274        } elsif ($self->{state} == MD_CDATA_STATE) {
2275          if ($self->{nc} == {
2276                '[' => 0x0043, # C
2277                '[C' => 0x0044, # D
2278                '[CD' => 0x0041, # A
2279                '[CDA' => 0x0054, # T
2280                '[CDAT' => 0x0041, # A
2281              }->{$self->{s_kwd}}) {
2282            !!!cp (135.1);
2283            ## Stay in the state.
2284            $self->{s_kwd} .= chr $self->{nc};
2285            !!!next-input-character;
2286            redo A;
2287          } elsif ($self->{s_kwd} eq '[CDATA' and
2288                   $self->{nc} == 0x005B) { # [
2289            !!!cp (135.2);
2290            $self->{ct} = {type => CHARACTER_TOKEN,
2291                                      data => '',
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 7};
2294            $self->{state} = CDATA_SECTION_STATE;
2295            !!!next-input-character;
2296            redo A;
2297          } else {
2298            !!!cp (135.3);
2299            !!!parse-error (type => 'bogus comment',
2300                            line => $self->{line_prev},
2301                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2302            $self->{state} = BOGUS_COMMENT_STATE;
2303            ## Reconsume.
2304            $self->{ct} = {type => COMMENT_TOKEN,
2305                                      data => $self->{s_kwd},
2306                                      line => $self->{line_prev},
2307                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2308                                     };
2309            redo A;
2310          }
2311      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2312        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2313          !!!cp (137);          !!!cp (137);
2314          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2315          !!!next-input-character;          !!!next-input-character;
2316          redo A;          redo A;
2317        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2318          !!!cp (138);          !!!cp (138);
2319          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2320          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2321          !!!next-input-character;          !!!next-input-character;
2322    
2323          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2324    
2325          redo A;          redo A;
2326        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2327          !!!cp (139);          !!!cp (139);
2328          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2329          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2330          ## reconsume          ## reconsume
2331    
2332          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2333    
2334          redo A;          redo A;
2335        } else {        } else {
2336          !!!cp (140);          !!!cp (140);
2337          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2338              .= chr ($self->{next_char});              .= chr ($self->{nc});
2339          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2340          !!!next-input-character;          !!!next-input-character;
2341          redo A;          redo A;
2342        }        }
2343      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2344        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2345          !!!cp (141);          !!!cp (141);
2346          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2347          !!!next-input-character;          !!!next-input-character;
2348          redo A;          redo A;
2349        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2350          !!!cp (142);          !!!cp (142);
2351          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2352          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2353          !!!next-input-character;          !!!next-input-character;
2354    
2355          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2356    
2357          redo A;          redo A;
2358        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2359          !!!cp (143);          !!!cp (143);
2360          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2361          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2362          ## reconsume          ## reconsume
2363    
2364          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2365    
2366          redo A;          redo A;
2367        } else {        } else {
2368          !!!cp (144);          !!!cp (144);
2369          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2370              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2371          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2372          !!!next-input-character;          !!!next-input-character;
2373          redo A;          redo A;
2374        }        }
2375      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2376        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2377          !!!cp (145);          !!!cp (145);
2378          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2379          !!!next-input-character;          !!!next-input-character;
2380          redo A;          redo A;
2381        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2382          !!!cp (146);          !!!cp (146);
2383          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2384          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2385          ## reconsume          ## reconsume
2386    
2387          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2388    
2389          redo A;          redo A;
2390        } else {        } else {
2391          !!!cp (147);          !!!cp (147);
2392          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2393            $self->{read_until}->($self->{ct}->{data},
2394                                  q[-],
2395                                  length $self->{ct}->{data});
2396    
2397          ## Stay in the state          ## Stay in the state
2398          !!!next-input-character;          !!!next-input-character;
2399          redo A;          redo A;
2400        }        }
2401      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2402        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2403          !!!cp (148);          !!!cp (148);
2404          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2405          !!!next-input-character;          !!!next-input-character;
2406          redo A;          redo A;
2407        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2408          !!!cp (149);          !!!cp (149);
2409          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2410          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2411          ## reconsume          ## reconsume
2412    
2413          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2414    
2415          redo A;          redo A;
2416        } else {        } else {
2417          !!!cp (150);          !!!cp (150);
2418          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2419          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2420          !!!next-input-character;          !!!next-input-character;
2421          redo A;          redo A;
2422        }        }
2423      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2424        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2425          !!!cp (151);          !!!cp (151);
2426          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2427          !!!next-input-character;          !!!next-input-character;
2428    
2429          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2430    
2431          redo A;          redo A;
2432        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2433          !!!cp (152);          !!!cp (152);
2434          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2435          $self->{current_token}->{data} .= '-'; # comment                          line => $self->{line_prev},
2436                            column => $self->{column_prev});
2437            $self->{ct}->{data} .= '-'; # comment
2438          ## Stay in the state          ## Stay in the state
2439          !!!next-input-character;          !!!next-input-character;
2440          redo A;          redo A;
2441        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2442          !!!cp (153);          !!!cp (153);
2443          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2444          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2445          ## reconsume          ## reconsume
2446    
2447          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2448    
2449          redo A;          redo A;
2450        } else {        } else {
2451          !!!cp (154);          !!!cp (154);
2452          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2453          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment                          line => $self->{line_prev},
2454                            column => $self->{column_prev});
2455            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2456          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2457          !!!next-input-character;          !!!next-input-character;
2458          redo A;          redo A;
2459        }        }
2460      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2461        if ($self->{next_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  
2462          !!!cp (155);          !!!cp (155);
2463          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2464          !!!next-input-character;          !!!next-input-character;
# Line 1645  sub _get_next_token ($) { Line 2471  sub _get_next_token ($) {
2471          redo A;          redo A;
2472        }        }
2473      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2474        if ($self->{next_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  
2475          !!!cp (157);          !!!cp (157);
2476          ## Stay in the state          ## Stay in the state
2477          !!!next-input-character;          !!!next-input-character;
2478          redo A;          redo A;
2479        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2480          !!!cp (158);          !!!cp (158);
2481          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2482          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2483          !!!next-input-character;          !!!next-input-character;
2484    
2485          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2486    
2487          redo A;          redo A;
2488        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2489          !!!cp (159);          !!!cp (159);
2490          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2491          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2492          ## reconsume          ## reconsume
2493    
2494          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2495    
2496          redo A;          redo A;
2497        } else {        } else {
2498          !!!cp (160);          !!!cp (160);
2499          $self->{current_token}          $self->{ct}->{name} = chr $self->{nc};
2500              = {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  
2501          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2502          !!!next-input-character;          !!!next-input-character;
2503          redo A;          redo A;
2504        }        }
2505      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2506  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2507        if ($self->{next_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  
2508          !!!cp (161);          !!!cp (161);
2509          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2510          !!!next-input-character;          !!!next-input-character;
2511          redo A;          redo A;
2512        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2513          !!!cp (162);          !!!cp (162);
2514          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2515          !!!next-input-character;          !!!next-input-character;
2516    
2517          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2518    
2519          redo A;          redo A;
2520        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2521          !!!cp (163);          !!!cp (163);
2522          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2523          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2524          ## reconsume          ## reconsume
2525    
2526          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2527          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2528    
2529          redo A;          redo A;
2530        } else {        } else {
2531          !!!cp (164);          !!!cp (164);
2532          $self->{current_token}->{name}          $self->{ct}->{name}
2533            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2534          ## Stay in the state          ## Stay in the state
2535          !!!next-input-character;          !!!next-input-character;
2536          redo A;          redo A;
2537        }        }
2538      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2539        if ($self->{next_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  
2540          !!!cp (165);          !!!cp (165);
2541          ## Stay in the state          ## Stay in the state
2542          !!!next-input-character;          !!!next-input-character;
2543          redo A;          redo A;
2544        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2545          !!!cp (166);          !!!cp (166);
2546          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2547          !!!next-input-character;          !!!next-input-character;
2548    
2549          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2550    
2551          redo A;          redo A;
2552        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2553          !!!cp (167);          !!!cp (167);
2554          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2555          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2556          ## reconsume          ## reconsume
2557    
2558          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2559          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2560    
2561          redo A;          redo A;
2562        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2563                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2564            $self->{state} = PUBLIC_STATE;
2565            $self->{s_kwd} = chr $self->{nc};
2566          !!!next-input-character;          !!!next-input-character;
2567          if ($self->{next_char} == 0x0055 or # U          redo A;
2568              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2569            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2570            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2571                $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  
2572          !!!next-input-character;          !!!next-input-character;
2573          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);  
         }  
   
         #  
2574        } else {        } else {
2575          !!!cp (180);          !!!cp (180);
2576            !!!parse-error (type => 'string after DOCTYPE name');
2577            $self->{ct}->{quirks} = 1;
2578    
2579            $self->{state} = BOGUS_DOCTYPE_STATE;
2580          !!!next-input-character;          !!!next-input-character;
2581          #          redo A;
2582        }        }
2583        } elsif ($self->{state} == PUBLIC_STATE) {
2584          ## ASCII case-insensitive
2585          if ($self->{nc} == [
2586                undef,
2587                0x0055, # U
2588                0x0042, # B
2589                0x004C, # L
2590                0x0049, # I
2591              ]->[length $self->{s_kwd}] or
2592              $self->{nc} == [
2593                undef,
2594                0x0075, # u
2595                0x0062, # b
2596                0x006C, # l
2597                0x0069, # i
2598              ]->[length $self->{s_kwd}]) {
2599            !!!cp (175);
2600            ## Stay in the state.
2601            $self->{s_kwd} .= chr $self->{nc};
2602            !!!next-input-character;
2603            redo A;
2604          } elsif ((length $self->{s_kwd}) == 5 and
2605                   ($self->{nc} == 0x0043 or # C
2606                    $self->{nc} == 0x0063)) { # c
2607            !!!cp (168);
2608            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2609            !!!next-input-character;
2610            redo A;
2611          } else {
2612            !!!cp (169);
2613            !!!parse-error (type => 'string after DOCTYPE name',
2614                            line => $self->{line_prev},
2615                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2616            $self->{ct}->{quirks} = 1;
2617    
2618        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2619        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2620            redo A;
2621          }
2622        } elsif ($self->{state} == SYSTEM_STATE) {
2623          ## ASCII case-insensitive
2624          if ($self->{nc} == [
2625                undef,
2626                0x0059, # Y
2627                0x0053, # S
2628                0x0054, # T
2629                0x0045, # E
2630              ]->[length $self->{s_kwd}] or
2631              $self->{nc} == [
2632                undef,
2633                0x0079, # y
2634                0x0073, # s
2635                0x0074, # t
2636                0x0065, # e
2637              ]->[length $self->{s_kwd}]) {
2638            !!!cp (170);
2639            ## Stay in the state.
2640            $self->{s_kwd} .= chr $self->{nc};
2641            !!!next-input-character;
2642            redo A;
2643          } elsif ((length $self->{s_kwd}) == 5 and
2644                   ($self->{nc} == 0x004D or # M
2645                    $self->{nc} == 0x006D)) { # m
2646            !!!cp (171);
2647            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2648            !!!next-input-character;
2649            redo A;
2650          } else {
2651            !!!cp (172);
2652            !!!parse-error (type => 'string after DOCTYPE name',
2653                            line => $self->{line_prev},
2654                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2655            $self->{ct}->{quirks} = 1;
2656    
2657        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2658        # next-input-character is already done          ## Reconsume.
2659        redo A;          redo A;
2660          }
2661      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2662        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2663          !!!cp (181);          !!!cp (181);
2664          ## Stay in the state          ## Stay in the state
2665          !!!next-input-character;          !!!next-input-character;
2666          redo A;          redo A;
2667        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2668          !!!cp (182);          !!!cp (182);
2669          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2670          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2671          !!!next-input-character;          !!!next-input-character;
2672          redo A;          redo A;
2673        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2674          !!!cp (183);          !!!cp (183);
2675          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2676          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2677          !!!next-input-character;          !!!next-input-character;
2678          redo A;          redo A;
2679        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2680          !!!cp (184);          !!!cp (184);
2681          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2682    
2683          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2684          !!!next-input-character;          !!!next-input-character;
2685    
2686          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2687          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2688    
2689          redo A;          redo A;
2690        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2691          !!!cp (185);          !!!cp (185);
2692          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2693    
2694          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2695          ## reconsume          ## reconsume
2696    
2697          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2698          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2699    
2700          redo A;          redo A;
2701        } else {        } else {
2702          !!!cp (186);          !!!cp (186);
2703          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2704          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2705    
2706          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2707          !!!next-input-character;          !!!next-input-character;
2708          redo A;          redo A;
2709        }        }
2710      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2711        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2712          !!!cp (187);          !!!cp (187);
2713          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2714          !!!next-input-character;          !!!next-input-character;
2715          redo A;          redo A;
2716        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2717          !!!cp (188);          !!!cp (188);
2718          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2719    
2720          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2721          !!!next-input-character;          !!!next-input-character;
2722    
2723          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2724          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2725    
2726          redo A;          redo A;
2727        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2728          !!!cp (189);          !!!cp (189);
2729          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2730    
2731          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2732          ## reconsume          ## reconsume
2733    
2734          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2735          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2736    
2737          redo A;          redo A;
2738        } else {        } else {
2739          !!!cp (190);          !!!cp (190);
2740          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2741              .= chr $self->{next_char};              .= chr $self->{nc};
2742            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2743                                  length $self->{ct}->{pubid});
2744    
2745          ## Stay in the state          ## Stay in the state
2746          !!!next-input-character;          !!!next-input-character;
2747          redo A;          redo A;
2748        }        }
2749      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2750        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2751          !!!cp (191);          !!!cp (191);
2752          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2753          !!!next-input-character;          !!!next-input-character;
2754          redo A;          redo A;
2755        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2756          !!!cp (192);          !!!cp (192);
2757          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2758    
2759          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2760          !!!next-input-character;          !!!next-input-character;
2761    
2762          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2763          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2764    
2765          redo A;          redo A;
2766        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2767          !!!cp (193);          !!!cp (193);
2768          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2769    
2770          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2771          ## reconsume          ## reconsume
2772    
2773          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2774          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2775    
2776          redo A;          redo A;
2777        } else {        } else {
2778          !!!cp (194);          !!!cp (194);
2779          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2780              .= chr $self->{next_char};              .= chr $self->{nc};
2781            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2782                                  length $self->{ct}->{pubid});
2783    
2784          ## Stay in the state          ## Stay in the state
2785          !!!next-input-character;          !!!next-input-character;
2786          redo A;          redo A;
2787        }        }
2788      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2789        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2790          !!!cp (195);          !!!cp (195);
2791          ## Stay in the state          ## Stay in the state
2792          !!!next-input-character;          !!!next-input-character;
2793          redo A;          redo A;
2794        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2795          !!!cp (196);          !!!cp (196);
2796          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2797          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2798          !!!next-input-character;          !!!next-input-character;
2799          redo A;          redo A;
2800        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2801          !!!cp (197);          !!!cp (197);
2802          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2803          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2804          !!!next-input-character;          !!!next-input-character;
2805          redo A;          redo A;
2806        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2807          !!!cp (198);          !!!cp (198);
2808          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2809          !!!next-input-character;          !!!next-input-character;
2810    
2811          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2812    
2813          redo A;          redo A;
2814        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2815          !!!cp (199);          !!!cp (199);
2816          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2817    
2818          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2819          ## reconsume          ## reconsume
2820    
2821          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2822          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2823    
2824          redo A;          redo A;
2825        } else {        } else {
2826          !!!cp (200);          !!!cp (200);
2827          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2828          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2829    
2830          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2831          !!!next-input-character;          !!!next-input-character;
2832          redo A;          redo A;
2833        }        }
2834      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2835        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2836          !!!cp (201);          !!!cp (201);
2837          ## Stay in the state          ## Stay in the state
2838          !!!next-input-character;          !!!next-input-character;
2839          redo A;          redo A;
2840        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2841          !!!cp (202);          !!!cp (202);
2842          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2843          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845          redo A;          redo A;
2846        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2847          !!!cp (203);          !!!cp (203);
2848          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2849          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2850          !!!next-input-character;          !!!next-input-character;
2851          redo A;          redo A;
2852        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2853          !!!cp (204);          !!!cp (204);
2854          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2855          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2856          !!!next-input-character;          !!!next-input-character;
2857    
2858          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2859          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2860    
2861          redo A;          redo A;
2862        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2863          !!!cp (205);          !!!cp (205);
2864          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2865    
2866          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2867          ## reconsume          ## reconsume
2868    
2869          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2870          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2871    
2872          redo A;          redo A;
2873        } else {        } else {
2874          !!!cp (206);          !!!cp (206);
2875          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2876          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2877    
2878          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880          redo A;          redo A;
2881        }        }
2882      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2883        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2884          !!!cp (207);          !!!cp (207);
2885          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2886          !!!next-input-character;          !!!next-input-character;
2887          redo A;          redo A;
2888        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2889          !!!cp (208);          !!!cp (208);
2890          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2891    
2892          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2893          !!!next-input-character;          !!!next-input-character;
2894    
2895          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2896          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2897    
2898          redo A;          redo A;
2899        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2900          !!!cp (209);          !!!cp (209);
2901          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2902    
2903          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2904          ## reconsume          ## reconsume
2905    
2906          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2907          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2908    
2909          redo A;          redo A;
2910        } else {        } else {
2911          !!!cp (210);          !!!cp (210);
2912          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2913              .= chr $self->{next_char};              .= chr $self->{nc};
2914            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2915                                  length $self->{ct}->{sysid});
2916    
2917          ## Stay in the state          ## Stay in the state
2918          !!!next-input-character;          !!!next-input-character;
2919          redo A;          redo A;
2920        }        }
2921      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2922        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2923          !!!cp (211);          !!!cp (211);
2924          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2925          !!!next-input-character;          !!!next-input-character;
2926          redo A;          redo A;
2927        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2928          !!!cp (212);          !!!cp (212);
2929          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2930    
2931          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2932          !!!next-input-character;          !!!next-input-character;
2933    
2934          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2935          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2936    
2937          redo A;          redo A;
2938        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2939          !!!cp (213);          !!!cp (213);
2940          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2941    
2942          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2943          ## reconsume          ## reconsume
2944    
2945          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2946          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2947    
2948          redo A;          redo A;
2949        } else {        } else {
2950          !!!cp (214);          !!!cp (214);
2951          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2952              .= chr $self->{next_char};              .= chr $self->{nc};
2953            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2954                                  length $self->{ct}->{sysid});
2955    
2956          ## Stay in the state          ## Stay in the state
2957          !!!next-input-character;          !!!next-input-character;
2958          redo A;          redo A;
2959        }        }
2960      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2961        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2962          !!!cp (215);          !!!cp (215);
2963          ## Stay in the state          ## Stay in the state
2964          !!!next-input-character;          !!!next-input-character;
2965          redo A;          redo A;
2966        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2967          !!!cp (216);          !!!cp (216);
2968          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2969          !!!next-input-character;          !!!next-input-character;
2970    
2971          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2972    
2973          redo A;          redo A;
2974        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2975          !!!cp (217);          !!!cp (217);
2976          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2977          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2978          ## reconsume          ## reconsume
2979    
2980          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2981          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2982    
2983          redo A;          redo A;
2984        } else {        } else {
2985          !!!cp (218);          !!!cp (218);
2986          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2987          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2988    
2989          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2990          !!!next-input-character;          !!!next-input-character;
2991          redo A;          redo A;
2992        }        }
2993      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2994        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2995          !!!cp (219);          !!!cp (219);
2996          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2997          !!!next-input-character;          !!!next-input-character;
2998    
2999          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
3000    
3001          redo A;          redo A;
3002        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
3003          !!!cp (220);          !!!cp (220);
         !!!parse-error (type => 'unclosed DOCTYPE');  
3004          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
3005          ## reconsume          ## reconsume
3006    
3007          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
3008    
3009          redo A;          redo A;
3010        } else {        } else {
3011          !!!cp (221);          !!!cp (221);
3012            my $s = '';
3013            $self->{read_until}->($s, q[>], 0);
3014    
3015          ## Stay in the state          ## Stay in the state
3016          !!!next-input-character;          !!!next-input-character;
3017          redo A;          redo A;
3018        }        }
3019      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3020        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3021      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3022    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3023          
3024    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3025  } # _get_next_token          !!!cp (221.1);
3026            $self->{state} = CDATA_SECTION_MSE1_STATE;
3027            !!!next-input-character;
3028            redo A;
3029          } elsif ($self->{nc} == -1) {
3030            $self->{state} = DATA_STATE;
3031            !!!next-input-character;
3032            if (length $self->{ct}->{data}) { # character
3033              !!!cp (221.2);
3034              !!!emit ($self->{ct}); # character
3035            } else {
3036              !!!cp (221.3);
3037              ## No token to emit. $self->{ct} is discarded.
3038            }        
3039            redo A;
3040          } else {
3041            !!!cp (221.4);
3042            $self->{ct}->{data} .= chr $self->{nc};
3043            $self->{read_until}->($self->{ct}->{data},
3044                                  q<]>,
3045                                  length $self->{ct}->{data});
3046    
3047  sub _tokenize_attempt_to_consume_an_entity ($$$) {          ## Stay in the state.
3048    my ($self, $in_attr, $additional) = @_;          !!!next-input-character;
3049            redo A;
3050          }
3051    
3052    if ({        ## ISSUE: "text tokens" in spec.
3053         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3054         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3055         $additional => 1,          !!!cp (221.5);
3056        }->{$self->{next_char}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3057      !!!cp (1001);          !!!next-input-character;
3058      ## Don't consume          redo A;
3059      ## No error        } else {
3060      return undef;          !!!cp (221.6);
3061    } elsif ($self->{next_char} == 0x0023) { # #          $self->{ct}->{data} .= ']';
3062      !!!next-input-character;          $self->{state} = CDATA_SECTION_STATE;
3063      if ($self->{next_char} == 0x0078 or # x          ## Reconsume.
3064          $self->{next_char} == 0x0058) { # X          redo A;
3065        my $code;        }
3066        X: {      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3067          my $x_char = $self->{next_char};        if ($self->{nc} == 0x003E) { # >
3068          !!!next-input-character;          $self->{state} = DATA_STATE;
3069          if (0x0030 <= $self->{next_char} and          !!!next-input-character;
3070              $self->{next_char} <= 0x0039) { # 0..9          if (length $self->{ct}->{data}) { # character
3071            !!!cp (1002);            !!!cp (221.7);
3072            $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;  
3073          } else {          } else {
3074            !!!cp (1007);            !!!cp (221.8);
3075            !!!parse-error (type => 'no refc');            ## No token to emit. $self->{ct} is discarded.
3076          }          }
3077            redo A;
3078          } elsif ($self->{nc} == 0x005D) { # ]
3079            !!!cp (221.9); # character
3080            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3081            ## Stay in the state.
3082            !!!next-input-character;
3083            redo A;
3084          } else {
3085            !!!cp (221.11);
3086            $self->{ct}->{data} .= ']]'; # character
3087            $self->{state} = CDATA_SECTION_STATE;
3088            ## Reconsume.
3089            redo A;
3090          }
3091        } elsif ($self->{state} == ENTITY_STATE) {
3092          if ($is_space->{$self->{nc}} or
3093              {
3094                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3095                $self->{entity_add} => 1,
3096              }->{$self->{nc}}) {
3097            !!!cp (1001);
3098            ## Don't consume
3099            ## No error
3100            ## Return nothing.
3101            #
3102          } elsif ($self->{nc} == 0x0023) { # #
3103            !!!cp (999);
3104            $self->{state} = ENTITY_HASH_STATE;
3105            $self->{s_kwd} = '#';
3106            !!!next-input-character;
3107            redo A;
3108          } elsif ((0x0041 <= $self->{nc} and
3109                    $self->{nc} <= 0x005A) or # A..Z
3110                   (0x0061 <= $self->{nc} and
3111                    $self->{nc} <= 0x007A)) { # a..z
3112            !!!cp (998);
3113            require Whatpm::_NamedEntityList;
3114            $self->{state} = ENTITY_NAME_STATE;
3115            $self->{s_kwd} = chr $self->{nc};
3116            $self->{entity__value} = $self->{s_kwd};
3117            $self->{entity__match} = 0;
3118            !!!next-input-character;
3119            redo A;
3120          } else {
3121            !!!cp (1027);
3122            !!!parse-error (type => 'bare ero');
3123            ## Return nothing.
3124            #
3125          }
3126    
3127          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        ## NOTE: No character is consumed by the "consume a character
3128            !!!cp (1008);        ## reference" algorithm.  In other word, there is an "&" character
3129            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        ## that does not introduce a character reference, which would be
3130            $code = 0xFFFD;        ## appended to the parent element or the attribute value in later
3131          } elsif ($code > 0x10FFFF) {        ## process of the tokenizer.
3132            !!!cp (1009);  
3133            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);        if ($self->{prev_state} == DATA_STATE) {
3134            $code = 0xFFFD;          !!!cp (997);
3135          } elsif ($code == 0x000D) {          $self->{state} = $self->{prev_state};
3136            !!!cp (1010);          ## Reconsume.
3137            !!!parse-error (type => 'CR character reference');          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3138            $code = 0x000A;                    line => $self->{line_prev},
3139          } elsif (0x80 <= $code and $code <= 0x9F) {                    column => $self->{column_prev},
3140            !!!cp (1011);                   });
3141            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          redo A;
3142            $code = $c1_entity_char->{$code};        } else {
3143          }          !!!cp (996);
3144            $self->{ca}->{value} .= '&';
3145          return {type => CHARACTER_TOKEN, data => chr $code,          $self->{state} = $self->{prev_state};
3146                  has_reference => 1};          ## Reconsume.
3147        } # X          redo A;
3148      } elsif (0x0030 <= $self->{next_char} and        }
3149               $self->{next_char} <= 0x0039) { # 0..9      } elsif ($self->{state} == ENTITY_HASH_STATE) {
3150        my $code = $self->{next_char} - 0x0030;        if ($self->{nc} == 0x0078 or # x
3151        !!!next-input-character;            $self->{nc} == 0x0058) { # X
3152                  !!!cp (995);
3153        while (0x0030 <= $self->{next_char} and          $self->{state} = HEXREF_X_STATE;
3154                  $self->{next_char} <= 0x0039) { # 0..9          $self->{s_kwd} .= chr $self->{nc};
3155            !!!next-input-character;
3156            redo A;
3157          } elsif (0x0030 <= $self->{nc} and
3158                   $self->{nc} <= 0x0039) { # 0..9
3159            !!!cp (994);
3160            $self->{state} = NCR_NUM_STATE;
3161            $self->{s_kwd} = $self->{nc} - 0x0030;
3162            !!!next-input-character;
3163            redo A;
3164          } else {
3165            !!!parse-error (type => 'bare nero',
3166                            line => $self->{line_prev},
3167                            column => $self->{column_prev} - 1);
3168    
3169            ## NOTE: According to the spec algorithm, nothing is returned,
3170            ## and then "&#" is appended to the parent element or the attribute
3171            ## value in the later processing.
3172    
3173            if ($self->{prev_state} == DATA_STATE) {
3174              !!!cp (1019);
3175              $self->{state} = $self->{prev_state};
3176              ## Reconsume.
3177              !!!emit ({type => CHARACTER_TOKEN,
3178                        data => '&#',
3179                        line => $self->{line_prev},
3180                        column => $self->{column_prev} - 1,
3181                       });
3182              redo A;
3183            } else {
3184              !!!cp (993);
3185              $self->{ca}->{value} .= '&#';
3186              $self->{state} = $self->{prev_state};
3187              ## Reconsume.
3188              redo A;
3189            }
3190          }
3191        } elsif ($self->{state} == NCR_NUM_STATE) {
3192          if (0x0030 <= $self->{nc} and
3193              $self->{nc} <= 0x0039) { # 0..9
3194          !!!cp (1012);          !!!cp (1012);
3195          $code *= 10;          $self->{s_kwd} *= 10;
3196          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3197                    
3198            ## Stay in the state.
3199          !!!next-input-character;          !!!next-input-character;
3200        }          redo A;
3201          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3202          !!!cp (1013);          !!!cp (1013);
3203          !!!next-input-character;          !!!next-input-character;
3204            #
3205        } else {        } else {
3206          !!!cp (1014);          !!!cp (1014);
3207          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3208            ## Reconsume.
3209            #
3210        }        }
3211    
3212        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3213          my $l = $self->{line_prev};
3214          my $c = $self->{column_prev};
3215          if ($charref_map->{$code}) {
3216          !!!cp (1015);          !!!cp (1015);
3217          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!parse-error (type => 'invalid character reference',
3218          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3219                            line => $l, column => $c);
3220            $code = $charref_map->{$code};
3221        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3222          !!!cp (1016);          !!!cp (1016);
3223          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!parse-error (type => 'invalid character reference',
3224                            text => (sprintf 'U-%08X', $code),
3225                            line => $l, column => $c);
3226          $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};  
3227        }        }
3228          
3229        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3230      } else {          !!!cp (992);
3231        !!!cp (1019);          $self->{state} = $self->{prev_state};
3232        !!!parse-error (type => 'bare nero');          ## Reconsume.
3233        !!!back-next-input-character ($self->{next_char});          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3234        $self->{next_char} = 0x0023; # #                    line => $l, column => $c,
3235        return undef;                   });
3236      }          redo A;
3237    } elsif ((0x0041 <= $self->{next_char} and        } else {
3238              $self->{next_char} <= 0x005A) or          !!!cp (991);
3239             (0x0061 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3240              $self->{next_char} <= 0x007A)) {          $self->{ca}->{has_reference} = 1;
3241      my $entity_name = chr $self->{next_char};          $self->{state} = $self->{prev_state};
3242      !!!next-input-character;          ## Reconsume.
3243            redo A;
3244      my $value = $entity_name;        }
3245      my $match = 0;      } elsif ($self->{state} == HEXREF_X_STATE) {
3246      require Whatpm::_NamedEntityList;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3247      our $EntityChar;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3248              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3249      while (length $entity_name < 10 and          # 0..9, A..F, a..f
3250             ## NOTE: Some number greater than the maximum length of entity name          !!!cp (990);
3251             ((0x0041 <= $self->{next_char} and # a          $self->{state} = HEXREF_HEX_STATE;
3252               $self->{next_char} <= 0x005A) or # x          $self->{s_kwd} = 0;
3253              (0x0061 <= $self->{next_char} and # a          ## Reconsume.
3254               $self->{next_char} <= 0x007A) or # z          redo A;
3255              (0x0030 <= $self->{next_char} and # 0        } else {
3256               $self->{next_char} <= 0x0039) or # 9          !!!parse-error (type => 'bare hcro',
3257              $self->{next_char} == 0x003B)) { # ;                          line => $self->{line_prev},
3258        $entity_name .= chr $self->{next_char};                          column => $self->{column_prev} - 2);
3259        if (defined $EntityChar->{$entity_name}) {  
3260          if ($self->{next_char} == 0x003B) { # ;          ## NOTE: According to the spec algorithm, nothing is returned,
3261            !!!cp (1020);          ## and then "&#" followed by "X" or "x" is appended to the parent
3262            $value = $EntityChar->{$entity_name};          ## element or the attribute value in the later processing.
3263            $match = 1;  
3264            !!!next-input-character;          if ($self->{prev_state} == DATA_STATE) {
3265            last;            !!!cp (1005);
3266              $self->{state} = $self->{prev_state};
3267              ## Reconsume.
3268              !!!emit ({type => CHARACTER_TOKEN,
3269                        data => '&' . $self->{s_kwd},
3270                        line => $self->{line_prev},
3271                        column => $self->{column_prev} - length $self->{s_kwd},
3272                       });
3273              redo A;
3274          } else {          } else {
3275            !!!cp (1021);            !!!cp (989);
3276            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3277            $match = -1;            $self->{state} = $self->{prev_state};
3278            !!!next-input-character;            ## Reconsume.
3279              redo A;
3280          }          }
3281        } else {        }
3282          !!!cp (1022);      } elsif ($self->{state} == HEXREF_HEX_STATE) {
3283          $value .= chr $self->{next_char};        if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3284          $match *= 2;          # 0..9
3285            !!!cp (1002);
3286            $self->{s_kwd} *= 0x10;
3287            $self->{s_kwd} += $self->{nc} - 0x0030;
3288            ## Stay in the state.
3289            !!!next-input-character;
3290            redo A;
3291          } elsif (0x0061 <= $self->{nc} and
3292                   $self->{nc} <= 0x0066) { # a..f
3293            !!!cp (1003);
3294            $self->{s_kwd} *= 0x10;
3295            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3296            ## Stay in the state.
3297            !!!next-input-character;
3298            redo A;
3299          } elsif (0x0041 <= $self->{nc} and
3300                   $self->{nc} <= 0x0046) { # A..F
3301            !!!cp (1004);
3302            $self->{s_kwd} *= 0x10;
3303            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3304            ## Stay in the state.
3305            !!!next-input-character;
3306            redo A;
3307          } elsif ($self->{nc} == 0x003B) { # ;
3308            !!!cp (1006);
3309          !!!next-input-character;          !!!next-input-character;
3310            #
3311          } else {
3312            !!!cp (1007);
3313            !!!parse-error (type => 'no refc',
3314                            line => $self->{line},
3315                            column => $self->{column});
3316            ## Reconsume.
3317            #
3318        }        }
3319      }  
3320              my $code = $self->{s_kwd};
3321      if ($match > 0) {        my $l = $self->{line_prev};
3322        !!!cp (1023);        my $c = $self->{column_prev};
3323        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($charref_map->{$code}) {
3324      } elsif ($match < 0) {          !!!cp (1008);
3325        !!!parse-error (type => 'no refc');          !!!parse-error (type => 'invalid character reference',
3326        if ($in_attr and $match < -1) {                          text => (sprintf 'U+%04X', $code),
3327          !!!cp (1024);                          line => $l, column => $c);
3328          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          $code = $charref_map->{$code};
3329          } elsif ($code > 0x10FFFF) {
3330            !!!cp (1009);
3331            !!!parse-error (type => 'invalid character reference',
3332                            text => (sprintf 'U-%08X', $code),
3333                            line => $l, column => $c);
3334            $code = 0xFFFD;
3335          }
3336    
3337          if ($self->{prev_state} == DATA_STATE) {
3338            !!!cp (988);
3339            $self->{state} = $self->{prev_state};
3340            ## Reconsume.
3341            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3342                      line => $l, column => $c,
3343                     });
3344            redo A;
3345          } else {
3346            !!!cp (987);
3347            $self->{ca}->{value} .= chr $code;
3348            $self->{ca}->{has_reference} = 1;
3349            $self->{state} = $self->{prev_state};
3350            ## Reconsume.
3351            redo A;
3352          }
3353        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3354          if (length $self->{s_kwd} < 30 and
3355              ## NOTE: Some number greater than the maximum length of entity name
3356              ((0x0041 <= $self->{nc} and # a
3357                $self->{nc} <= 0x005A) or # x
3358               (0x0061 <= $self->{nc} and # a
3359                $self->{nc} <= 0x007A) or # z
3360               (0x0030 <= $self->{nc} and # 0
3361                $self->{nc} <= 0x0039) or # 9
3362               $self->{nc} == 0x003B)) { # ;
3363            our $EntityChar;
3364            $self->{s_kwd} .= chr $self->{nc};
3365            if (defined $EntityChar->{$self->{s_kwd}}) {
3366              if ($self->{nc} == 0x003B) { # ;
3367                !!!cp (1020);
3368                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3369                $self->{entity__match} = 1;
3370                !!!next-input-character;
3371                #
3372              } else {
3373                !!!cp (1021);
3374                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3375                $self->{entity__match} = -1;
3376                ## Stay in the state.
3377                !!!next-input-character;
3378                redo A;
3379              }
3380            } else {
3381              !!!cp (1022);
3382              $self->{entity__value} .= chr $self->{nc};
3383              $self->{entity__match} *= 2;
3384              ## Stay in the state.
3385              !!!next-input-character;
3386              redo A;
3387            }
3388          }
3389    
3390          my $data;
3391          my $has_ref;
3392          if ($self->{entity__match} > 0) {
3393            !!!cp (1023);
3394            $data = $self->{entity__value};
3395            $has_ref = 1;
3396            #
3397          } elsif ($self->{entity__match} < 0) {
3398            !!!parse-error (type => 'no refc');
3399            if ($self->{prev_state} != DATA_STATE and # in attribute
3400                $self->{entity__match} < -1) {
3401              !!!cp (1024);
3402              $data = '&' . $self->{s_kwd};
3403              #
3404            } else {
3405              !!!cp (1025);
3406              $data = $self->{entity__value};
3407              $has_ref = 1;
3408              #
3409            }
3410        } else {        } else {
3411          !!!cp (1025);          !!!cp (1026);
3412          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!parse-error (type => 'bare ero',
3413                            line => $self->{line_prev},
3414                            column => $self->{column_prev} - length $self->{s_kwd});
3415            $data = '&' . $self->{s_kwd};
3416            #
3417          }
3418      
3419          ## NOTE: In these cases, when a character reference is found,
3420          ## it is consumed and a character token is returned, or, otherwise,
3421          ## nothing is consumed and returned, according to the spec algorithm.
3422          ## In this implementation, anything that has been examined by the
3423          ## tokenizer is appended to the parent element or the attribute value
3424          ## as string, either literal string when no character reference or
3425          ## entity-replaced string otherwise, in this stage, since any characters
3426          ## that would not be consumed are appended in the data state or in an
3427          ## appropriate attribute value state anyway.
3428    
3429          if ($self->{prev_state} == DATA_STATE) {
3430            !!!cp (986);
3431            $self->{state} = $self->{prev_state};
3432            ## Reconsume.
3433            !!!emit ({type => CHARACTER_TOKEN,
3434                      data => $data,
3435                      line => $self->{line_prev},
3436                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3437                     });
3438            redo A;
3439          } else {
3440            !!!cp (985);
3441            $self->{ca}->{value} .= $data;
3442            $self->{ca}->{has_reference} = 1 if $has_ref;
3443            $self->{state} = $self->{prev_state};
3444            ## Reconsume.
3445            redo A;
3446        }        }
3447      } else {      } else {
3448        !!!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};  
3449      }      }
3450    } else {    } # A  
3451      !!!cp (1027);  
3452      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3453      !!!parse-error (type => 'bare ero');  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3454    
3455  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3456    my $self = shift;    my $self = shift;
# Line 2402  sub _initialize_tree_constructor ($) { Line 3459  sub _initialize_tree_constructor ($) {
3459    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3460    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3461    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3462      $self->{document}->set_user_data (manakai_source_line => 1);
3463      $self->{document}->set_user_data (manakai_source_column => 1);
3464  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3465    
3466  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2421  sub _construct_tree ($) { Line 3480  sub _construct_tree ($) {
3480    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3481    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3482    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
3483        
3484    !!!next-token;    !!!next-token;
3485    
3486    undef $self->{form_element};    undef $self->{form_element};
3487    undef $self->{head_element};    undef $self->{head_element};
3488      undef $self->{head_element_inserted};
3489    $self->{open_elements} = [];    $self->{open_elements} = [];
3490    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3491      undef $self->{ignore_newline};
3492    
3493    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
3494    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 2456  sub _tree_construction_initial ($) { Line 3513  sub _tree_construction_initial ($) {
3513        ## language.        ## language.
3514        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3515        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3516        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3517        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3518            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3519          !!!cp ('t1');          !!!cp ('t1');
3520          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3521        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3522          !!!cp ('t2');          !!!cp ('t2');
3523          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!parse-error (type => 'not HTML5', token => $token);
3524          !!!parse-error (type => 'not HTML5');        } elsif (defined $token->{pubid}) {
3525            if ($token->{pubid} eq 'XSLT-compat') {
3526              !!!cp ('t1.2');
3527              !!!parse-error (type => 'XSLT-compat', token => $token,
3528                              level => $self->{level}->{should});
3529            } else {
3530              !!!parse-error (type => 'not HTML5', token => $token);
3531            }
3532        } else {        } else {
3533          !!!cp ('t3');          !!!cp ('t3');
3534            #
3535        }        }
3536                
3537        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3538          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3539        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3540            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3541        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3542            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3543        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3544        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3545        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 2483  sub _tree_construction_initial ($) { Line 3547  sub _tree_construction_initial ($) {
3547        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3548          !!!cp ('t4');          !!!cp ('t4');
3549          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3550        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3551          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3552          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3553          if ({          my $prefix = [
3554            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3555            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3556            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3557            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3558            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3559            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3560            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3561            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3562            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3563            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3564            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3565            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3566            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3567            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3568            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3569            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3570            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3571            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3572            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3573            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3574            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3575            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3576            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3577            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3578            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3579            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3580            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3581            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3582            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3583            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3584            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3585            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3586            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3587            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3588            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3589            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3590            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3591            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3592            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3593            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3594            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3595            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3596            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3597            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3598            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3599            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3600            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3601            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3602            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3603            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3604            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3605            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3606            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3607            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3608            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3609            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3610            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3611            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3612            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3613            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3614            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3615            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3616            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3617            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3618            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3619            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3620            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
           "-//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}) {  
3621            !!!cp ('t5');            !!!cp ('t5');
3622            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3623          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3624                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3625            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3626              !!!cp ('t6');              !!!cp ('t6');
3627              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3628            } else {            } else {
3629              !!!cp ('t7');              !!!cp ('t7');
3630              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3631            }            }
3632          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3633                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3634            !!!cp ('t8');            !!!cp ('t8');
3635            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3636          } else {          } else {
# Line 2581  sub _tree_construction_initial ($) { Line 3639  sub _tree_construction_initial ($) {
3639        } else {        } else {
3640          !!!cp ('t10');          !!!cp ('t10');
3641        }        }
3642        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3643          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3644          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3645          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3646            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3647              ## marked as quirks.
3648            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3649            !!!cp ('t11');            !!!cp ('t11');
3650          } else {          } else {
# Line 2604  sub _tree_construction_initial ($) { Line 3663  sub _tree_construction_initial ($) {
3663                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3664               }->{$token->{type}}) {               }->{$token->{type}}) {
3665        !!!cp ('t14');        !!!cp ('t14');
3666        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3667        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3668        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3669        ## reprocess        ## reprocess
3670          !!!ack-later;
3671        return;        return;
3672      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3673        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3674          ## Ignore the token          ## Ignore the token
3675    
3676          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 2625  sub _tree_construction_initial ($) { Line 3685  sub _tree_construction_initial ($) {
3685          !!!cp ('t17');          !!!cp ('t17');
3686        }        }
3687    
3688        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3689        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3690        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3691        ## reprocess        ## reprocess
# Line 2654  sub _tree_construction_root_element ($) Line 3714  sub _tree_construction_root_element ($)
3714    B: {    B: {
3715        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3716          !!!cp ('t19');          !!!cp ('t19');
3717          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3718          ## Ignore the token          ## Ignore the token
3719          ## Stay in the insertion mode.          ## Stay in the insertion mode.
3720          !!!next-token;          !!!next-token;
# Line 2667  sub _tree_construction_root_element ($) Line 3727  sub _tree_construction_root_element ($)
3727          !!!next-token;          !!!next-token;
3728          redo B;          redo B;
3729        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3730          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3731            ## Ignore the token.            ## Ignore the token.
3732    
3733            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 2688  sub _tree_construction_root_element ($) Line 3748  sub _tree_construction_root_element ($)
3748        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3749          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3750            my $root_element;            my $root_element;
3751            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes});            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3752            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3753            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3754                  [$root_element, $el_category->{html}];
3755    
3756            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3757              !!!cp ('t24');              !!!cp ('t24');
3758              $self->{application_cache_selection}              $self->{application_cache_selection}
3759                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3760              ## ISSUE: No relative reference resolution?              ## ISSUE: Spec is unclear on relative references.
3761                ## According to Hixie (#whatwg 2008-03-19), it should be
3762                ## resolved against the base URI of the document in HTML
3763                ## or xml:base of the element in XHTML.
3764            } else {            } else {
3765              !!!cp ('t25');              !!!cp ('t25');
3766              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3767            }            }
3768    
3769              !!!nack ('t25c');
3770    
3771            !!!next-token;            !!!next-token;
3772            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3773          } else {          } else {
# Line 2718  sub _tree_construction_root_element ($) Line 3784  sub _tree_construction_root_element ($)
3784          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3785        }        }
3786    
3787      my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3788        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3789      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3790      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3791    
3792      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3793    
3794      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3795        !!!ack-later;
3796      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3797    } # B    } # B
3798    
3799    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 2748  sub _reset_insertion_mode ($) { Line 3814  sub _reset_insertion_mode ($) {
3814        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3815          $last = 1;          $last = 1;
3816          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3817            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3818                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3819              !!!cp ('t27');          } else {
3820              #            die "_reset_insertion_mode: t27";
           } else {  
             !!!cp ('t28');  
             $node = $self->{inner_html_node};  
           }  
3821          }          }
3822        }        }
3823              
3824        ## Step 4..13        ## Step 4..14
3825        my $new_mode = {        my $new_mode;
3826          if ($node->[1] & FOREIGN_EL) {
3827            !!!cp ('t28.1');
3828            ## NOTE: Strictly spaking, the line below only applies to MathML and
3829            ## SVG elements.  Currently the HTML syntax supports only MathML and
3830            ## SVG elements as foreigners.
3831            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3832          } elsif ($node->[1] == TABLE_CELL_EL) {
3833            if ($last) {
3834              !!!cp ('t28.2');
3835              #
3836            } else {
3837              !!!cp ('t28.3');
3838              $new_mode = IN_CELL_IM;
3839            }
3840          } else {
3841            !!!cp ('t28.4');
3842            $new_mode = {
3843                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3844                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3845                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
3846                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3847                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3848                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2776  sub _reset_insertion_mode ($) { Line 3853  sub _reset_insertion_mode ($) {
3853                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3854                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3855                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3856                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3857          }
3858        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3859                
3860        ## Step 14        ## Step 15
3861        if ($node->[1] eq 'html') {        if ($node->[1] == HTML_EL) {
3862          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3863            !!!cp ('t29');            !!!cp ('t29');
3864            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2794  sub _reset_insertion_mode ($) { Line 3872  sub _reset_insertion_mode ($) {
3872          !!!cp ('t31');          !!!cp ('t31');
3873        }        }
3874                
3875        ## Step 15        ## Step 16
3876        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3877                
3878        ## Step 16        ## Step 17
3879        $i--;        $i--;
3880        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3881                
3882        ## Step 17        ## Step 18
3883        redo S3;        redo S3;
3884      } # S3      } # S3
3885    
# Line 2912  sub _tree_construction_main ($) { Line 3990  sub _tree_construction_main ($) {
3990    
3991      ## Step 1      ## Step 1
3992      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3993      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $start_tag_name, $token->{attributes});  
3994    
3995      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
3996      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3997      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3998    
3999      ## Step 4      ## Step 3, 4
4000      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       !!!cp ('t40');  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
4001    
4002      ## Step 7      !!!nack ('t40.1');
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } else {  
       ## NOTE: An end-of-file token.  
       if ($content_model_flag == CDATA_CONTENT_MODEL) {  
         !!!cp ('t43');  
         !!!parse-error (type => 'in CDATA:#'.$token->{type});  
       } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
         !!!cp ('t44');  
         !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
       } else {  
         die "$0: $content_model_flag in parse_rcdata";  
       }  
     }  
4003      !!!next-token;      !!!next-token;
4004    }; # $parse_rcdata    }; # $parse_rcdata
4005    
4006    my $script_start_tag = sub () {    my $script_start_tag = sub () {
4007        ## Step 1
4008      my $script_el;      my $script_el;
4009      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4010    
4011        ## Step 2
4012      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4013    
4014        ## Step 3
4015        ## TODO: Mark as "already executed", if ...
4016    
4017        ## Step 4
4018        $insert->($script_el);
4019    
4020        ## ISSUE: $script_el is not put into the stack
4021        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4022    
4023        ## Step 5
4024      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4025      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
       
     my $text = '';  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) {  
       !!!cp ('t45');  
       $text .= $token->{data};  
       !!!next-token;  
     } # stop if non-character token or tokenizer stops tokenising  
     if (length $text) {  
       !!!cp ('t46');  
       $script_el->manakai_append_text ($text);  
     }  
                 
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
4026    
4027      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
4028          $token->{tag_name} eq 'script') {      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
       !!!cp ('t47');  
       ## Ignore the token  
     } else {  
       !!!cp ('t48');  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
       ## ISSUE: And ignore?  
       ## TODO: mark as "already executed"  
     }  
       
     if (defined $self->{inner_html_node}) {  
       !!!cp ('t49');  
       ## TODO: mark as "already executed"  
     } else {  
       !!!cp ('t50');  
       ## TODO: $old_insertion_point = current insertion point  
       ## TODO: insertion point = just before the next input character  
4029    
4030        $insert->($script_el);      !!!nack ('t40.2');
         
       ## TODO: insertion point = $old_insertion_point (might be "undefined")  
         
       ## TODO: if there is a script that will execute as soon as the parser resume, then...  
     }  
       
4031      !!!next-token;      !!!next-token;
4032    }; # $script_start_tag    }; # $script_start_tag
4033    
4034    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4035    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4036      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4037    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4038    
4039    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4040      my $tag_name = shift;      my $end_tag_token = shift;
4041        my $tag_name = $end_tag_token->{tag_name};
4042    
4043      ## NOTE: The adoptioon agency algorithm (AAA).      ## NOTE: The adoption agency algorithm (AAA).
4044    
4045      FET: {      FET: {
4046        ## Step 1        ## Step 1
4047        my $formatting_element;        my $formatting_element;
4048        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4049        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4050          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4051              !!!cp ('t52');
4052              last AFE;
4053            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4054                         eq $tag_name) {
4055            !!!cp ('t51');            !!!cp ('t51');
4056            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4057            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4058            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
4059          }          }
4060        } # AFE        } # AFE
4061        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4062          !!!cp ('t53');          !!!cp ('t53');
4063          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4064          ## Ignore the token          ## Ignore the token
4065          !!!next-token;          !!!next-token;
4066          return;          return;
# Line 3055  sub _tree_construction_main ($) { Line 4077  sub _tree_construction_main ($) {
4077              last INSCOPE;              last INSCOPE;
4078            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4079              !!!cp ('t55');              !!!cp ('t55');
4080              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
4081                                text => $token->{tag_name},
4082                                token => $end_tag_token);
4083              ## Ignore the token              ## Ignore the token
4084              !!!next-token;              !!!next-token;
4085              return;              return;
4086            }            }
4087          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4088            !!!cp ('t56');            !!!cp ('t56');
4089            $in_scope = 0;            $in_scope = 0;
4090          }          }
4091        } # INSCOPE        } # INSCOPE
4092        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4093          !!!cp ('t57');          !!!cp ('t57');
4094          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!parse-error (type => 'unmatched end tag',
4095                            text => $token->{tag_name},
4096                            token => $end_tag_token);
4097          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4098          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4099          return;          return;
4100        }        }
4101        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4102          !!!cp ('t58');          !!!cp ('t58');
4103          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!parse-error (type => 'not closed',
4104                            text => $self->{open_elements}->[-1]->[0]
4105                                ->manakai_local_name,
4106                            token => $end_tag_token);
4107        }        }
4108                
4109        ## Step 2        ## Step 2
# Line 3085  sub _tree_construction_main ($) { Line 4111  sub _tree_construction_main ($) {
4111        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4112        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4113          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4114          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4115              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4116              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4117               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4118            !!!cp ('t59');            !!!cp ('t59');
4119            $furthest_block = $node;            $furthest_block = $node;
4120            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4121              ## NOTE: The topmost (eldest) node.
4122          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4123            !!!cp ('t60');            !!!cp ('t60');
4124            last OE;            last OE;
# Line 3174  sub _tree_construction_main ($) { Line 4201  sub _tree_construction_main ($) {
4201        } # S7          } # S7  
4202                
4203        ## Step 8        ## Step 8
4204        if ({        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
            table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
           }->{$common_ancestor_node->[1]}) {  
4205          my $foster_parent_element;          my $foster_parent_element;
4206          my $next_sibling;          my $next_sibling;
4207                           OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
4208                             if ($self->{open_elements}->[$_]->[1] eq 'table') {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
4209                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4210                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4211                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 3240  sub _tree_construction_main ($) { Line 4265  sub _tree_construction_main ($) {
4265            $i = $_;            $i = $_;
4266          }          }
4267        } # OE        } # OE
4268        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4269                
4270        ## Step 14        ## Step 14
4271        redo FET;        redo FET;
# Line 3253  sub _tree_construction_main ($) { Line 4278  sub _tree_construction_main ($) {
4278    
4279    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4280      my $child = shift;      my $child = shift;
4281      if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
          table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
         }->{$self->{open_elements}->[-1]->[1]}) {  
4282        # MUST        # MUST
4283        my $foster_parent_element;        my $foster_parent_element;
4284        my $next_sibling;        my $next_sibling;
4285                           OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4286                             if ($self->{open_elements}->[$_]->[1] eq 'table') {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
4287                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4288                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4289                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3285  sub _tree_construction_main ($) { Line 4308  sub _tree_construction_main ($) {
4308      }      }
4309    }; # $insert_to_foster    }; # $insert_to_foster
4310    
4311    B: {    ## NOTE: Insert a character (MUST): When a character is inserted, if
4312      ## the last node that was inserted by the parser is a Text node and
4313      ## the character has to be inserted after that node, then the
4314      ## character is appended to the Text node.  However, if any other
4315      ## node is inserted by the parser, then a new Text node is created
4316      ## and the character is appended as that Text node.  If I'm not
4317      ## wrong, for a parser with scripting disabled, there are only two
4318      ## cases where this occurs.  One is the case where an element node
4319      ## is inserted to the |head| element.  This is covered by using the
4320      ## |$self->{head_element_inserted}| flag.  Another is the case where
4321      ## an element or comment is inserted into the |table| subtree while
4322      ## foster parenting happens.  This is covered by using the [2] flag
4323      ## of the |$open_tables| structure.  All other cases are handled
4324      ## simply by calling |manakai_append_text| method.
4325    
4326      ## TODO: |<body><script>document.write("a<br>");
4327      ## document.body.removeChild (document.body.lastChild);
4328      ## document.write ("b")</script>|
4329    
4330      B: while (1) {
4331      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4332        !!!cp ('t73');        !!!cp ('t73');
4333        !!!parse-error (type => 'DOCTYPE in the middle');        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4334        ## Ignore the token        ## Ignore the token
4335        ## Stay in the phase        ## Stay in the phase
4336        !!!next-token;        !!!next-token;
4337        redo B;        next B;
     } elsif ($token->{type} == END_OF_FILE_TOKEN) {  
       if ($self->{insertion_mode} & AFTER_HTML_IMS) {  
         !!!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;  
4338      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4339               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4340        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4341          !!!cp ('t79');          !!!cp ('t79');
4342          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4343          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4344        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4345          !!!cp ('t80');          !!!cp ('t80');
4346          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4347          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4348        } else {        } else {
4349          !!!cp ('t81');          !!!cp ('t81');
4350        }        }
4351    
4352        !!!cp ('t82');        !!!cp ('t82');
4353        !!!parse-error (type => 'not first start tag');        !!!parse-error (type => 'not first start tag', token => $token);
4354        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4355        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4356          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
# Line 3350  sub _tree_construction_main ($) { Line 4360  sub _tree_construction_main ($) {
4360               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4361          }          }
4362        }        }
4363          !!!nack ('t84.1');
4364        !!!next-token;        !!!next-token;
4365        redo B;        next B;
4366      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4367        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4368        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3363  sub _tree_construction_main ($) { Line 4374  sub _tree_construction_main ($) {
4374        } else {        } else {
4375          !!!cp ('t87');          !!!cp ('t87');
4376          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4377            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4378        }        }
4379        !!!next-token;        !!!next-token;
4380        redo B;        next B;
4381      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4382          if ($token->{type} == CHARACTER_TOKEN) {
4383            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4384            delete $self->{ignore_newline};
4385    
4386            if (length $token->{data}) {
4387              !!!cp ('t43');
4388              $self->{open_elements}->[-1]->[0]->manakai_append_text
4389                  ($token->{data});
4390            } else {
4391              !!!cp ('t43.1');
4392            }
4393            !!!next-token;
4394            next B;
4395          } elsif ($token->{type} == END_TAG_TOKEN) {
4396            delete $self->{ignore_newline};
4397    
4398            if ($token->{tag_name} eq 'script') {
4399              !!!cp ('t50');
4400              
4401              ## Para 1-2
4402              my $script = pop @{$self->{open_elements}};
4403              
4404              ## Para 3
4405              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4406    
4407              ## Para 4
4408              ## TODO: $old_insertion_point = $current_insertion_point;
4409              ## TODO: $current_insertion_point = just before $self->{nc};
4410    
4411              ## Para 5
4412              ## TODO: Run the $script->[0].
4413    
4414              ## Para 6
4415              ## TODO: $current_insertion_point = $old_insertion_point;
4416    
4417              ## Para 7
4418              ## TODO: if ($pending_external_script) {
4419                ## TODO: ...
4420              ## TODO: }
4421    
4422              !!!next-token;
4423              next B;
4424            } else {
4425              !!!cp ('t42');
4426    
4427              pop @{$self->{open_elements}};
4428    
4429              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4430              !!!next-token;
4431              next B;
4432            }
4433          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4434            delete $self->{ignore_newline};
4435    
4436            !!!cp ('t44');
4437            !!!parse-error (type => 'not closed',
4438                            text => $self->{open_elements}->[-1]->[0]
4439                                ->manakai_local_name,
4440                            token => $token);
4441    
4442            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
4443            #  ## TODO: Mark as "already executed"
4444            #}
4445    
4446            pop @{$self->{open_elements}};
4447    
4448            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4449            ## Reprocess.
4450            next B;
4451          } else {
4452            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
4453          }
4454        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4455        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4456          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t87.1');
4457            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4458            !!!next-token;
4459            next B;
4460          } elsif ($token->{type} == START_TAG_TOKEN) {
4461            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4462                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4463                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4464                ($token->{tag_name} eq 'svg' and
4465                 $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
4466              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4467              !!!cp ('t87.2');
4468              #
4469            } elsif ({
4470                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4471                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4472                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4473                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4474                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4475                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4476                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4477                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4478                     }->{$token->{tag_name}}) {
4479              !!!cp ('t87.2');
4480              !!!parse-error (type => 'not closed',
4481                              text => $self->{open_elements}->[-1]->[0]
4482                                  ->manakai_local_name,
4483                              token => $token);
4484    
4485              pop @{$self->{open_elements}}
4486                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4487    
4488              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4489              ## Reprocess.
4490              next B;
4491            } else {
4492              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4493              my $tag_name = $token->{tag_name};
4494              if ($nsuri eq $SVG_NS) {
4495                $tag_name = {
4496                   altglyph => 'altGlyph',
4497                   altglyphdef => 'altGlyphDef',
4498                   altglyphitem => 'altGlyphItem',
4499                   animatecolor => 'animateColor',
4500                   animatemotion => 'animateMotion',
4501                   animatetransform => 'animateTransform',
4502                   clippath => 'clipPath',
4503                   feblend => 'feBlend',
4504                   fecolormatrix => 'feColorMatrix',
4505                   fecomponenttransfer => 'feComponentTransfer',
4506                   fecomposite => 'feComposite',
4507                   feconvolvematrix => 'feConvolveMatrix',
4508                   fediffuselighting => 'feDiffuseLighting',
4509                   fedisplacementmap => 'feDisplacementMap',
4510                   fedistantlight => 'feDistantLight',
4511                   feflood => 'feFlood',
4512                   fefunca => 'feFuncA',
4513                   fefuncb => 'feFuncB',
4514                   fefuncg => 'feFuncG',
4515                   fefuncr => 'feFuncR',
4516                   fegaussianblur => 'feGaussianBlur',
4517                   feimage => 'feImage',
4518                   femerge => 'feMerge',
4519                   femergenode => 'feMergeNode',
4520                   femorphology => 'feMorphology',
4521                   feoffset => 'feOffset',
4522                   fepointlight => 'fePointLight',
4523                   fespecularlighting => 'feSpecularLighting',
4524                   fespotlight => 'feSpotLight',
4525                   fetile => 'feTile',
4526                   feturbulence => 'feTurbulence',
4527                   foreignobject => 'foreignObject',
4528                   glyphref => 'glyphRef',
4529                   lineargradient => 'linearGradient',
4530                   radialgradient => 'radialGradient',
4531                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4532                   textpath => 'textPath',  
4533                }->{$tag_name} || $tag_name;
4534              }
4535    
4536              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4537    
4538              ## "adjust foreign attributes" - done in insert-element-f
4539    
4540              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4541    
4542              if ($self->{self_closing}) {
4543                pop @{$self->{open_elements}};
4544                !!!ack ('t87.3');
4545              } else {
4546                !!!cp ('t87.4');
4547              }
4548    
4549              !!!next-token;
4550              next B;
4551            }
4552          } elsif ($token->{type} == END_TAG_TOKEN) {
4553            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4554            !!!cp ('t87.5');
4555            #
4556          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4557            !!!cp ('t87.6');
4558            !!!parse-error (type => 'not closed',
4559                            text => $self->{open_elements}->[-1]->[0]
4560                                ->manakai_local_name,
4561                            token => $token);
4562    
4563            pop @{$self->{open_elements}}
4564                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4565    
4566            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4567    
4568            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4569            ## Reprocess.
4570            next B;
4571          } else {
4572            die "$0: $token->{type}: Unknown token type";        
4573          }
4574        }
4575    
4576        if ($self->{insertion_mode} & HEAD_IMS) {
4577          if ($token->{type} == CHARACTER_TOKEN) {
4578            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4579            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4580              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
4581              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
4582                  $self->{open_elements}->[-1]->[0]->append_child
4583                    ($self->{document}->create_text_node ($1));
4584                  delete $self->{head_element_inserted};
4585                  ## NOTE: |</head> <link> |
4586                  #
4587                } else {
4588                  !!!cp ('t88.2');
4589                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4590                  ## NOTE: |</head> &#x20;|
4591                  #
4592                }
4593            } else {            } else {
4594              !!!cp ('t88.1');              !!!cp ('t88.1');
4595              ## Ignore the token.              ## Ignore the token.
4596              !!!next-token;              #
             redo B;  
4597            }            }
4598            unless (length $token->{data}) {            unless (length $token->{data}) {
4599              !!!cp ('t88');              !!!cp ('t88');
4600              !!!next-token;              !!!next-token;
4601              redo B;              next B;
4602            }            }
4603    ## TODO: set $token->{column} appropriately
4604          }          }
4605    
4606          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4607            !!!cp ('t89');            !!!cp ('t89');
4608            ## As if <head>            ## As if <head>
4609            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4610            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4611            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4612                  [$self->{head_element}, $el_category->{head}];
4613    
4614            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4615            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3400  sub _tree_construction_main ($) { Line 4619  sub _tree_construction_main ($) {
4619            !!!cp ('t90');            !!!cp ('t90');
4620            ## As if </noscript>            ## As if </noscript>
4621            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4622            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4623                        
4624            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4625            ## As if </head>            ## As if </head>
# Line 3416  sub _tree_construction_main ($) { Line 4635  sub _tree_construction_main ($) {
4635            !!!cp ('t92');            !!!cp ('t92');
4636          }          }
4637    
4638              ## "after head" insertion mode          ## "after head" insertion mode
4639              ## As if <body>          ## As if <body>
4640              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4641              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4642              ## reprocess          ## reprocess
4643              redo B;          next B;
4644            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4645              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4646                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4647                  !!!cp ('t93');              !!!cp ('t93');
4648                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4649                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4650                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4651                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4652                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4653                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4654                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4655                  !!!cp ('t94');              !!!next-token;
4656                  #              next B;
4657                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4658                  !!!cp ('t95');              !!!cp ('t93.2');
4659                  !!!parse-error (type => 'in head:head'); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4660                  ## Ignore the token                              token => $token);
4661                  !!!next-token;              ## Ignore the token
4662                  redo B;              !!!nack ('t93.3');
4663                }              !!!next-token;
4664              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4665                !!!cp ('t96');            } else {
4666                ## As if <head>              !!!cp ('t95');
4667                !!!create-element ($self->{head_element}, 'head');              !!!parse-error (type => 'in head:head',
4668                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4669                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4670                !!!nack ('t95.1');
4671                !!!next-token;
4672                next B;
4673              }
4674            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4675              !!!cp ('t96');
4676              ## As if <head>
4677              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4678              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4679              push @{$self->{open_elements}},
4680                  [$self->{head_element}, $el_category->{head}];
4681    
4682                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4683                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4684              } else {          } else {
4685                !!!cp ('t97');            !!!cp ('t97');
4686              }          }
4687    
4688              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4689                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4690                  !!!cp ('t98');              !!!cp ('t98');
4691                  ## As if </noscript>              ## As if </noscript>
4692                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4693                  !!!parse-error (type => 'in noscript:base');              !!!parse-error (type => 'in noscript', text => 'base',
4694                                              token => $token);
4695                  $self->{insertion_mode} = IN_HEAD_IM;            
4696                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4697                } else {              ## Reprocess in the "in head" insertion mode...
4698                  !!!cp ('t99');            } else {
4699                }              !!!cp ('t99');
4700              }
4701    
4702                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4703                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4704                  !!!cp ('t100');              !!!cp ('t100');
4705                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4706                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4707                } else {              push @{$self->{open_elements}},
4708                  !!!cp ('t101');                  [$self->{head_element}, $el_category->{head}];
4709                }              $self->{head_element_inserted} = 1;
4710                !!!insert-element ($token->{tag_name}, $token->{attributes});            } else {
4711                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              !!!cp ('t101');
4712                pop @{$self->{open_elements}} # <head>            }
4713                    if $self->{insertion_mode} == AFTER_HEAD_IM;            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4714                !!!next-token;            pop @{$self->{open_elements}};
4715                redo B;            pop @{$self->{open_elements}} # <head>
4716              } elsif ($token->{tag_name} eq 'link') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4717                ## NOTE: There is a "as if in head" code clone.            !!!nack ('t101.1');
4718                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            !!!next-token;
4719                  !!!cp ('t102');            next B;
4720                  !!!parse-error (type => 'after head:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'link') {
4721                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            ## NOTE: There is a "as if in head" code clone.
4722                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4723                  !!!cp ('t103');              !!!cp ('t102');
4724                }              !!!parse-error (type => 'after head',
4725                !!!insert-element ($token->{tag_name}, $token->{attributes});                              text => $token->{tag_name}, token => $token);
4726                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              push @{$self->{open_elements}},
4727                pop @{$self->{open_elements}} # <head>                  [$self->{head_element}, $el_category->{head}];
4728                    if $self->{insertion_mode} == AFTER_HEAD_IM;              $self->{head_element_inserted} = 1;
4729                !!!next-token;            } else {
4730                redo B;              !!!cp ('t103');
4731              } elsif ($token->{tag_name} eq 'meta') {            }
4732                ## NOTE: There is a "as if in head" code clone.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4733                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}};
4734                  !!!cp ('t104');            pop @{$self->{open_elements}} # <head>
4735                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4736                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!ack ('t103.1');
4737                } else {            !!!next-token;
4738                  !!!cp ('t105');            next B;
4739                }          } elsif ($token->{tag_name} eq 'command' or
4740                !!!insert-element ($token->{tag_name}, $token->{attributes});                   $token->{tag_name} eq 'eventsource') {
4741                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            if ($self->{insertion_mode} == IN_HEAD_IM) {
4742                ## NOTE: If the insertion mode at the time of the emission
4743                ## of the token was "before head", $self->{insertion_mode}
4744                ## is already changed to |IN_HEAD_IM|.
4745    
4746                ## NOTE: There is a "as if in head" code clone.
4747                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4748                pop @{$self->{open_elements}};
4749                pop @{$self->{open_elements}} # <head>
4750                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4751                !!!ack ('t103.2');
4752                !!!next-token;
4753                next B;
4754              } else {
4755                ## NOTE: "in head noscript" or "after head" insertion mode
4756                ## - in these cases, these tags are treated as same as
4757                ## normal in-body tags.
4758                !!!cp ('t103.3');
4759                #
4760              }
4761            } elsif ($token->{tag_name} eq 'meta') {
4762              ## NOTE: There is a "as if in head" code clone.
4763              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4764                !!!cp ('t104');
4765                !!!parse-error (type => 'after head',
4766                                text => $token->{tag_name}, token => $token);
4767                push @{$self->{open_elements}},
4768                    [$self->{head_element}, $el_category->{head}];
4769                $self->{head_element_inserted} = 1;
4770              } else {
4771                !!!cp ('t105');
4772              }
4773              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4774              my $meta_el = pop @{$self->{open_elements}};
4775    
4776                unless ($self->{confident}) {                unless ($self->{confident}) {
4777                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4778                    !!!cp ('t106');                    !!!cp ('t106');
4779                      ## NOTE: Whether the encoding is supported or not is handled
4780                      ## in the {change_encoding} callback.
4781                    $self->{change_encoding}                    $self->{change_encoding}
4782                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4783                             $token);
4784                                        
4785                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4786                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4787                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4788                                                 ->{has_reference});                                                 ->{has_reference});
4789                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4790                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4791                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4792                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4793                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4794                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4795                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4796                      !!!cp ('t107');                      !!!cp ('t107');
4797                        ## NOTE: Whether the encoding is supported or not is handled
4798                        ## in the {change_encoding} callback.
4799                      $self->{change_encoding}                      $self->{change_encoding}
4800                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4801                               $token);
4802                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4803                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4804                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
# Line 3556  sub _tree_construction_main ($) { Line 4826  sub _tree_construction_main ($) {
4826    
4827                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4828                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4829                  !!!ack ('t110.1');
4830                !!!next-token;                !!!next-token;
4831                redo B;                next B;
4832              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4833                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4834                  !!!cp ('t111');              !!!cp ('t111');
4835                  ## As if </noscript>              ## As if </noscript>
4836                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4837                  !!!parse-error (type => 'in noscript:title');              !!!parse-error (type => 'in noscript', text => 'title',
4838                                              token => $token);
4839                  $self->{insertion_mode} = IN_HEAD_IM;            
4840                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4841                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4842                  !!!cp ('t112');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4843                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t112');
4844                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4845                } else {                              text => $token->{tag_name}, token => $token);
4846                  !!!cp ('t113');              push @{$self->{open_elements}},
4847                }                  [$self->{head_element}, $el_category->{head}];
4848                $self->{head_element_inserted} = 1;
4849                ## NOTE: There is a "as if in head" code clone.            } else {
4850                my $parent = defined $self->{head_element} ? $self->{head_element}              !!!cp ('t113');
4851                    : $self->{open_elements}->[-1]->[0];            }
4852                $parse_rcdata->(RCDATA_CONTENT_MODEL);  
4853                pop @{$self->{open_elements}} # <head>            ## NOTE: There is a "as if in head" code clone.
4854                    if $self->{insertion_mode} == AFTER_HEAD_IM;            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4855                redo B;            ## ISSUE: A spec bug [Bug 6038]
4856              } elsif ($token->{tag_name} eq 'style') {            splice @{$self->{open_elements}}, -2, 1, () # <head>
4857                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4858                ## insertion mode IN_HEAD_IM)            next B;
4859                ## NOTE: There is a "as if in head" code clone.          } elsif ($token->{tag_name} eq 'style' or
4860                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                   $token->{tag_name} eq 'noframes') {
4861                  !!!cp ('t114');            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4862                  !!!parse-error (type => 'after head:'.$token->{tag_name});            ## insertion mode IN_HEAD_IM)
4863                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            ## NOTE: There is a "as if in head" code clone.
4864                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4865                  !!!cp ('t115');              !!!cp ('t114');
4866                }              !!!parse-error (type => 'after head',
4867                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
4868                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
4869                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
4870                redo B;              $self->{head_element_inserted} = 1;
4871              } elsif ($token->{tag_name} eq 'noscript') {            } else {
4872                !!!cp ('t115');
4873              }
4874              $parse_rcdata->(CDATA_CONTENT_MODEL);
4875              ## ISSUE: A spec bug [Bug 6038]
4876              splice @{$self->{open_elements}}, -2, 1, () # <head>
4877                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4878              next B;
4879            } elsif ($token->{tag_name} eq 'noscript') {
4880                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4881                  !!!cp ('t116');                  !!!cp ('t116');
4882                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4883                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4884                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4885                    !!!nack ('t116.1');
4886                  !!!next-token;                  !!!next-token;
4887                  redo B;                  next B;
4888                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4889                  !!!cp ('t117');                  !!!cp ('t117');
4890                  !!!parse-error (type => 'in noscript:noscript');                  !!!parse-error (type => 'in noscript', text => 'noscript',
4891                                    token => $token);
4892                  ## Ignore the token                  ## Ignore the token
4893                    !!!nack ('t117.1');
4894                  !!!next-token;                  !!!next-token;
4895                  redo B;                  next B;
4896                } else {                } else {
4897                  !!!cp ('t118');                  !!!cp ('t118');
4898                  #                  #
4899                }                }
4900              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4901                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4902                  !!!cp ('t119');              !!!cp ('t119');
4903                  ## As if </noscript>              ## As if </noscript>
4904                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4905                  !!!parse-error (type => 'in noscript:script');              !!!parse-error (type => 'in noscript', text => 'script',
4906                                              token => $token);
4907                  $self->{insertion_mode} = IN_HEAD_IM;            
4908                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4909                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4910                  !!!cp ('t120');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4911                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t120');
4912                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4913                } else {                              text => $token->{tag_name}, token => $token);
4914                  !!!cp ('t121');              push @{$self->{open_elements}},
4915                }                  [$self->{head_element}, $el_category->{head}];
4916                $self->{head_element_inserted} = 1;
4917                ## NOTE: There is a "as if in head" code clone.            } else {
4918                $script_start_tag->();              !!!cp ('t121');
4919                pop @{$self->{open_elements}} # <head>            }
4920                    if $self->{insertion_mode} == AFTER_HEAD_IM;  
4921                redo B;            ## NOTE: There is a "as if in head" code clone.
4922              } elsif ($token->{tag_name} eq 'body' or            $script_start_tag->();
4923                       $token->{tag_name} eq 'frameset') {            ## ISSUE: A spec bug  [Bug 6038]
4924              splice @{$self->{open_elements}}, -2, 1 # <head>
4925                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4926              next B;
4927            } elsif ($token->{tag_name} eq 'body' or
4928                     $token->{tag_name} eq 'frameset') {
4929                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4930                  !!!cp ('t122');                  !!!cp ('t122');
4931                  ## As if </noscript>                  ## As if </noscript>
4932                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4933                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4934                                    text => $token->{tag_name}, token => $token);
4935                                    
4936                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4937                  ## As if </head>                  ## As if </head>
# Line 3660  sub _tree_construction_main ($) { Line 4948  sub _tree_construction_main ($) {
4948                }                }
4949    
4950                ## "after head" insertion mode                ## "after head" insertion mode
4951                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4952                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4953                  !!!cp ('t126');                  !!!cp ('t126');
4954                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
# Line 3670  sub _tree_construction_main ($) { Line 4958  sub _tree_construction_main ($) {
4958                } else {                } else {
4959                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4960                }                }
4961                  !!!nack ('t127.1');
4962                !!!next-token;                !!!next-token;
4963                redo B;                next B;
4964              } else {              } else {
4965                !!!cp ('t128');                !!!cp ('t128');
4966                #                #
# Line 3681  sub _tree_construction_main ($) { Line 4970  sub _tree_construction_main ($) {
4970                !!!cp ('t129');                !!!cp ('t129');
4971                ## As if </noscript>                ## As if </noscript>
4972                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4973                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4974                                  text => $token->{tag_name}, token => $token);
4975                                
4976                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4977                ## As if </head>                ## As if </head>
# Line 3700  sub _tree_construction_main ($) { Line 4990  sub _tree_construction_main ($) {
4990    
4991              ## "after head" insertion mode              ## "after head" insertion mode
4992              ## As if <body>              ## As if <body>
4993              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4994              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4995              ## reprocess              ## reprocess
4996              redo B;              !!!ack-later;
4997                next B;
4998            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4999              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
5000                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5001                  !!!cp ('t132');                  !!!cp ('t132');
5002                  ## As if <head>                  ## As if <head>
5003                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5004                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5005                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
5006                        [$self->{head_element}, $el_category->{head}];
5007    
5008                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5009                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5010                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5011                  !!!next-token;                  !!!next-token;
5012                  redo B;                  next B;
5013                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5014                  !!!cp ('t133');                  !!!cp ('t133');
5015                  ## As if </noscript>                  ## As if </noscript>
5016                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5017                  !!!parse-error (type => 'in noscript:/head');                  !!!parse-error (type => 'in noscript:/',
5018                                    text => 'head', token => $token);
5019                                    
5020                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5021                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5022                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5023                  !!!next-token;                  !!!next-token;
5024                  redo B;                  next B;
5025                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5026                  !!!cp ('t134');                  !!!cp ('t134');
5027                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5028                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5029                  !!!next-token;                  !!!next-token;
5030                  redo B;                  next B;
5031                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5032                    !!!cp ('t134.1');
5033                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5034                                    token => $token);
5035                    ## Ignore the token
5036                    !!!next-token;
5037                    next B;
5038                } else {                } else {
5039                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
5040                }                }
5041              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5042                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3745  sub _tree_construction_main ($) { Line 5044  sub _tree_construction_main ($) {
5044                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5045                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5046                  !!!next-token;                  !!!next-token;
5047                  redo B;                  next B;
5048                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5049                           $self->{insertion_mode} == AFTER_HEAD_IM) {
5050                  !!!cp ('t137');                  !!!cp ('t137');
5051                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!parse-error (type => 'unmatched end tag',
5052                                    text => 'noscript', token => $token);
5053                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5054                  !!!next-token;                  !!!next-token;
5055                  redo B;                  next B;
5056                } else {                } else {
5057                  !!!cp ('t138');                  !!!cp ('t138');
5058                  #                  #
# Line 3759  sub _tree_construction_main ($) { Line 5060  sub _tree_construction_main ($) {
5060              } elsif ({              } elsif ({
5061                        body => 1, html => 1,                        body => 1, html => 1,
5062                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5063                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                ## TODO: This branch is entirely redundant.
5064                  !!!cp ('t139');                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5065                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_IM or
5066                  !!!create-element ($self->{head_element}, 'head');                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
   
                 $self->{insertion_mode} = IN_HEAD_IM;  
                 ## Reprocess in the "in head" insertion mode...  
               } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
5067                  !!!cp ('t140');                  !!!cp ('t140');
5068                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5069                                    text => $token->{tag_name}, token => $token);
5070                    ## Ignore the token
5071                    !!!next-token;
5072                    next B;
5073                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5074                    !!!cp ('t140.1');
5075                    !!!parse-error (type => 'unmatched end tag',
5076                                    text => $token->{tag_name}, token => $token);
5077                  ## Ignore the token                  ## Ignore the token
5078                  !!!next-token;                  !!!next-token;
5079                  redo B;                  next B;
5080                } else {                } else {
5081                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5082                }                }
5083                              } elsif ($token->{tag_name} eq 'p') {
5084                #                !!!cp ('t142');
5085              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5086                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5087                       }->{$token->{tag_name}}) {                ## Ignore the token
5088                  !!!next-token;
5089                  next B;
5090                } elsif ($token->{tag_name} eq 'br') {
5091                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5092                  !!!cp ('t142');                  !!!cp ('t142.2');
5093                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5094                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5095                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5096                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5097      
5098                    ## Reprocess in the "after head" insertion mode...
5099                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5100                    !!!cp ('t143.2');
5101                    ## As if </head>
5102                    pop @{$self->{open_elements}};
5103                    $self->{insertion_mode} = AFTER_HEAD_IM;
5104      
5105                    ## Reprocess in the "after head" insertion mode...
5106                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5107                    !!!cp ('t143.3');
5108                    ## ISSUE: Two parse errors for <head><noscript></br>
5109                    !!!parse-error (type => 'unmatched end tag',
5110                                    text => 'br', token => $token);
5111                    ## As if </noscript>
5112                    pop @{$self->{open_elements}};
5113                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5114    
5115                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5116                } else {                  ## As if </head>
5117                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5118                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5119    
5120                #                  ## Reprocess in the "after head" insertion mode...
5121              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5122                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5123                  #                  #
5124                } else {                } else {
5125                  !!!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;  
5126                }                }
5127    
5128                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5129                  !!!parse-error (type => 'unmatched end tag',
5130                                  text => 'br', token => $token);
5131                  ## Ignore the token
5132                  !!!next-token;
5133                  next B;
5134                } else {
5135                  !!!cp ('t145');
5136                  !!!parse-error (type => 'unmatched end tag',
5137                                  text => $token->{tag_name}, token => $token);
5138                  ## Ignore the token
5139                  !!!next-token;
5140                  next B;
5141              }              }
5142    
5143              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5144                !!!cp ('t146');                !!!cp ('t146');
5145                ## As if </noscript>                ## As if </noscript>
5146                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5147                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5148                                  text => $token->{tag_name}, token => $token);
5149                                
5150                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5151                ## As if </head>                ## As if </head>
# Line 3829  sub _tree_construction_main ($) { Line 5161  sub _tree_construction_main ($) {
5161              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5162  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5163                !!!cp ('t148');                !!!cp ('t148');
5164                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5165                                  text => $token->{tag_name}, token => $token);
5166                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5167                !!!next-token;                !!!next-token;
5168                redo B;                next B;
5169              } else {              } else {
5170                !!!cp ('t149');                !!!cp ('t149');
5171              }              }
5172    
5173              ## "after head" insertion mode              ## "after head" insertion mode
5174              ## As if <body>              ## As if <body>
5175              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5176              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5177              ## reprocess              ## reprocess
5178              redo B;              next B;
5179            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5180              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5181            }            !!!cp ('t149.1');
5182    
5183              ## NOTE: As if <head>
5184              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5185              $self->{open_elements}->[-1]->[0]->append_child
5186                  ($self->{head_element});
5187              #push @{$self->{open_elements}},
5188              #    [$self->{head_element}, $el_category->{head}];
5189              #$self->{insertion_mode} = IN_HEAD_IM;
5190              ## NOTE: Reprocess.
5191    
5192              ## NOTE: As if </head>
5193              #pop @{$self->{open_elements}};
5194              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5195              ## NOTE: Reprocess.
5196              
5197              #
5198            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5199              !!!cp ('t149.2');
5200    
5201              ## NOTE: As if </head>
5202              pop @{$self->{open_elements}};
5203              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5204              ## NOTE: Reprocess.
5205    
5206              #
5207            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5208              !!!cp ('t149.3');
5209    
5210              !!!parse-error (type => 'in noscript:#eof', token => $token);
5211    
5212              ## As if </noscript>
5213              pop @{$self->{open_elements}};
5214              #$self->{insertion_mode} = IN_HEAD_IM;
5215              ## NOTE: Reprocess.
5216    
5217              ## NOTE: As if </head>
5218              pop @{$self->{open_elements}};
5219              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5220              ## NOTE: Reprocess.
5221    
5222              #
5223            } else {
5224              !!!cp ('t149.4');
5225              #
5226            }
5227    
5228            ## ISSUE: An issue in the spec.          ## NOTE: As if <body>
5229            !!!insert-element ('body',, $token);
5230            $self->{insertion_mode} = IN_BODY_IM;
5231            ## NOTE: Reprocess.
5232            next B;
5233          } else {
5234            die "$0: $token->{type}: Unknown token type";
5235          }
5236      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5237            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5238              !!!cp ('t150');              !!!cp ('t150');
# Line 3857  sub _tree_construction_main ($) { Line 5242  sub _tree_construction_main ($) {
5242              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5243    
5244              !!!next-token;              !!!next-token;
5245              redo B;              next B;
5246            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5247              if ({              if ({
5248                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3865  sub _tree_construction_main ($) { Line 5250  sub _tree_construction_main ($) {
5250                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5251                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5252                  ## have an element in table scope                  ## have an element in table scope
5253                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5254                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5255                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] == TABLE_CELL_EL) {
5256                      !!!cp ('t151');                      !!!cp ('t151');
5257                      $tn = $node->[1];  
5258                      last INSCOPE;                      ## Close the cell
5259                    } elsif ({                      !!!back-token; # <x>
5260                              table => 1, html => 1,                      $token = {type => END_TAG_TOKEN,
5261                             }->{$node->[1]}) {                                tag_name => $node->[0]->manakai_local_name,
5262                                  line => $token->{line},
5263                                  column => $token->{column}};
5264                        next B;
5265                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5266                      !!!cp ('t152');                      !!!cp ('t152');
5267                      last INSCOPE;                      ## ISSUE: This case can never be reached, maybe.
5268                    }                      last;
                 } # INSCOPE  
                   unless (defined $tn) {  
                     !!!cp ('t153');  
 ## TODO: This error type is wrong.  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
5269                    }                    }
5270                                    }
5271                  !!!cp ('t154');  
5272                  ## Close the cell                  !!!cp ('t153');
5273                  !!!back-token; # <?>                  !!!parse-error (type => 'start tag not allowed',
5274                  $token = {type => END_TAG_TOKEN, tag_name => $tn};                      text => $token->{tag_name}, token => $token);
5275                  redo B;                  ## Ignore the token
5276                    !!!nack ('t153.1');
5277                    !!!next-token;
5278                    next B;
5279                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5280                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5281                                    token => $token);
5282                                    
5283                  ## As if </caption>                  ## NOTE: As if </caption>.
5284                  ## have a table element in table scope                  ## have a table element in table scope
5285                  my $i;                  my $i;
5286                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5287                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5288                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5289                      !!!cp ('t155');                      if ($node->[1] == CAPTION_EL) {
5290                      $i = $_;                        !!!cp ('t155');
5291                      last INSCOPE;                        $i = $_;
5292                    } elsif ({                        last INSCOPE;
5293                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5294                             }->{$node->[1]}) {                        !!!cp ('t156');
5295                      !!!cp ('t156');                        last;
5296                      last INSCOPE;                      }
5297                    }                    }
5298    
5299                      !!!cp ('t157');
5300                      !!!parse-error (type => 'start tag not allowed',
5301                                      text => $token->{tag_name}, token => $token);
5302                      ## Ignore the token
5303                      !!!nack ('t157.1');
5304                      !!!next-token;
5305                      next B;
5306                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t157');  
 ## TODO: this type is wrong.  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5307                                    
5308                  ## generate implied end tags                  ## generate implied end tags
5309                  while ({                  while ($self->{open_elements}->[-1]->[1]
5310                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5311                    !!!cp ('t158');                    !!!cp ('t158');
5312                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5313                  }                  }
5314    
5315                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5316                    !!!cp ('t159');                    !!!cp ('t159');
5317                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5318                                      text => $self->{open_elements}->[-1]->[0]
5319                                          ->manakai_local_name,
5320                                      token => $token);
5321                  } else {                  } else {
5322                    !!!cp ('t160');                    !!!cp ('t160');
5323                  }                  }
# Line 3943  sub _tree_construction_main ($) { Line 5329  sub _tree_construction_main ($) {
5329                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5330                                    
5331                  ## reprocess                  ## reprocess
5332                  redo B;                  !!!ack-later;
5333                    next B;
5334                } else {                } else {
5335                  !!!cp ('t161');                  !!!cp ('t161');
5336                  #                  #
# Line 3959  sub _tree_construction_main ($) { Line 5346  sub _tree_construction_main ($) {
5346                  my $i;                  my $i;
5347                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5348                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5349                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5350                      !!!cp ('t163');                      !!!cp ('t163');
5351                      $i = $_;                      $i = $_;
5352                      last INSCOPE;                      last INSCOPE;
5353                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5354                      !!!cp ('t164');                      !!!cp ('t164');
5355                      last INSCOPE;                      last INSCOPE;
5356                    }                    }
5357                  } # INSCOPE                  } # INSCOPE
5358                    unless (defined $i) {                    unless (defined $i) {
5359                      !!!cp ('t165');                      !!!cp ('t165');
5360                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
5361                                        text => $token->{tag_name},
5362                                        token => $token);
5363                      ## Ignore the token                      ## Ignore the token
5364                      !!!next-token;                      !!!next-token;
5365                      redo B;                      next B;
5366                    }                    }
5367                                    
5368                  ## generate implied end tags                  ## generate implied end tags
5369                  while ({                  while ($self->{open_elements}->[-1]->[1]
5370                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5371                    !!!cp ('t166');                    !!!cp ('t166');
5372                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5373                  }                  }
5374    
5375                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5376                            ne $token->{tag_name}) {
5377                    !!!cp ('t167');                    !!!cp ('t167');
5378                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5379                                      text => $self->{open_elements}->[-1]->[0]
5380                                          ->manakai_local_name,
5381                                      token => $token);
5382                  } else {                  } else {
5383                    !!!cp ('t168');                    !!!cp ('t168');
5384                  }                  }
# Line 4000  sub _tree_construction_main ($) { Line 5390  sub _tree_construction_main ($) {
5390                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5391                                    
5392                  !!!next-token;                  !!!next-token;
5393                  redo B;                  next B;
5394                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5395                  !!!cp ('t169');                  !!!cp ('t169');
5396                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5397                                    text => $token->{tag_name}, token => $token);
5398                  ## Ignore the token                  ## Ignore the token
5399                  !!!next-token;                  !!!next-token;
5400                  redo B;                  next B;
5401                } else {                } else {
5402                  !!!cp ('t170');                  !!!cp ('t170');
5403                  #                  #
# Line 4015  sub _tree_construction_main ($) { Line 5406  sub _tree_construction_main ($) {
5406                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5407                  ## have a table element in table scope                  ## have a table element in table scope
5408                  my $i;                  my $i;
5409                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5410                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5411                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5412                      !!!cp ('t171');                      if ($node->[1] == CAPTION_EL) {
5413                      $i = $_;                        !!!cp ('t171');
5414                      last INSCOPE;                        $i = $_;
5415                    } elsif ({                        last INSCOPE;
5416                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5417                             }->{$node->[1]}) {                        !!!cp ('t172');
5418                      !!!cp ('t172');                        last;
5419                      last INSCOPE;                      }
5420                    }                    }
5421    
5422                      !!!cp ('t173');
5423                      !!!parse-error (type => 'unmatched end tag',
5424                                      text => $token->{tag_name}, token => $token);
5425                      ## Ignore the token
5426                      !!!next-token;
5427                      next B;
5428                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t173');  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5429                                    
5430                  ## generate implied end tags                  ## generate implied end tags
5431                  while ({                  while ($self->{open_elements}->[-1]->[1]
5432                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5433                    !!!cp ('t174');                    !!!cp ('t174');
5434                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5435                  }                  }
5436                                    
5437                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5438                    !!!cp ('t175');                    !!!cp ('t175');
5439                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5440                                      text => $self->{open_elements}->[-1]->[0]
5441                                          ->manakai_local_name,
5442                                      token => $token);
5443                  } else {                  } else {
5444                    !!!cp ('t176');                    !!!cp ('t176');
5445                  }                  }
# Line 4058  sub _tree_construction_main ($) { Line 5451  sub _tree_construction_main ($) {
5451                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5452                                    
5453                  !!!next-token;                  !!!next-token;
5454                  redo B;                  next B;
5455                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5456                  !!!cp ('t177');                  !!!cp ('t177');
5457                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5458                                    text => $token->{tag_name}, token => $token);
5459                  ## Ignore the token                  ## Ignore the token
5460                  !!!next-token;                  !!!next-token;
5461                  redo B;                  next B;
5462                } else {                } else {
5463                  !!!cp ('t178');                  !!!cp ('t178');
5464                  #                  #
# Line 4077  sub _tree_construction_main ($) { Line 5471  sub _tree_construction_main ($) {
5471                ## have an element in table scope                ## have an element in table scope
5472                my $i;                my $i;
5473                my $tn;                my $tn;
5474                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5475                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5476                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5477                    !!!cp ('t179');                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5478                    $i = $_;                      !!!cp ('t179');
5479                    last INSCOPE;                      $i = $_;
5480                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
5481                    !!!cp ('t180');                      ## Close the cell
5482                    $tn = $node->[1];                      !!!back-token; # </x>
5483                    ## NOTE: There is exactly one |td| or |th| element                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5484                    ## in scope in the stack of open elements by definition.                                line => $token->{line},
5485                  } elsif ({                                column => $token->{column}};
5486                            table => 1, html => 1,                      next B;
5487                           }->{$node->[1]}) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
5488                    !!!cp ('t181');                      !!!cp ('t180');
5489                    last INSCOPE;                      $tn = $node->[0]->manakai_local_name;
5490                        ## NOTE: There is exactly one |td| or |th| element
5491                        ## in scope in the stack of open elements by definition.
5492                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5493                        ## ISSUE: Can this be reached?
5494                        !!!cp ('t181');
5495                        last;
5496                      }
5497                  }                  }
5498                } # INSCOPE  
               unless (defined $i) {  
5499                  !!!cp ('t182');                  !!!cp ('t182');
5500                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5501                        text => $token->{tag_name}, token => $token);
5502                  ## Ignore the token                  ## Ignore the token
5503                  !!!next-token;                  !!!next-token;
5504                  redo B;                  next B;
5505                } else {                } # INSCOPE
                 !!!cp ('t183');  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5506              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5507                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5508                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5509                                  token => $token);
5510    
5511                ## As if </caption>                ## As if </caption>
5512                ## have a table element in table scope                ## have a table element in table scope
5513                my $i;                my $i;
5514                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5515                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5516                  if ($node->[1] eq 'caption') {                  if ($node->[1] == CAPTION_EL) {
5517                    !!!cp ('t184');                    !!!cp ('t184');
5518                    $i = $_;                    $i = $_;
5519                    last INSCOPE;                    last INSCOPE;
5520                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5521                    !!!cp ('t185');                    !!!cp ('t185');
5522                    last INSCOPE;                    last INSCOPE;
5523                  }                  }
5524                } # INSCOPE                } # INSCOPE
5525                unless (defined $i) {                unless (defined $i) {
5526                  !!!cp ('t186');                  !!!cp ('t186');
5527                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'unmatched end tag',
5528                                    text => 'caption', token => $token);
5529                  ## Ignore the token                  ## Ignore the token
5530                  !!!next-token;                  !!!next-token;
5531                  redo B;                  next B;
5532                }                }
5533                                
5534                ## generate implied end tags                ## generate implied end tags
5535                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5536                  !!!cp ('t187');                  !!!cp ('t187');
5537                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5538                }                }
5539    
5540                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5541                  !!!cp ('t188');                  !!!cp ('t188');
5542                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5543                                    text => $self->{open_elements}->[-1]->[0]
5544                                        ->manakai_local_name,
5545                                    token => $token);
5546                } else {                } else {
5547                  !!!cp ('t189');                  !!!cp ('t189');
5548                }                }
# Line 4159  sub _tree_construction_main ($) { Line 5554  sub _tree_construction_main ($) {
5554                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5555    
5556                ## reprocess                ## reprocess
5557                redo B;                next B;
5558              } elsif ({              } elsif ({
5559                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5560                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5561                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5562                  !!!cp ('t190');                  !!!cp ('t190');
5563                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5564                                    text => $token->{tag_name}, token => $token);
5565                  ## Ignore the token                  ## Ignore the token
5566                  !!!next-token;                  !!!next-token;
5567                  redo B;                  next B;
5568                } else {                } else {
5569                  !!!cp ('t191');                  !!!cp ('t191');
5570                  #                  #
# Line 4179  sub _tree_construction_main ($) { Line 5575  sub _tree_construction_main ($) {
5575                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5576                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5577                !!!cp ('t192');                !!!cp ('t192');
5578                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5579                                  text => $token->{tag_name}, token => $token);
5580                ## Ignore the token                ## Ignore the token
5581                !!!next-token;                !!!next-token;
5582                redo B;                next B;
5583              } else {              } else {
5584                !!!cp ('t193');                !!!cp ('t193');
5585                #                #
5586              }              }
5587          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5588            for my $entry (@{$self->{open_elements}}) {
5589              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5590                !!!cp ('t75');
5591                !!!parse-error (type => 'in body:#eof', token => $token);
5592                last;
5593              }
5594            }
5595    
5596            ## Stop parsing.
5597            last B;
5598        } else {        } else {
5599          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5600        }        }
# Line 4196  sub _tree_construction_main ($) { Line 5604  sub _tree_construction_main ($) {
5604      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5605        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5606          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5607              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5608            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5609                                
5610            unless (length $token->{data}) {            unless (length $token->{data}) {
5611              !!!cp ('t194');              !!!cp ('t194');
5612              !!!next-token;              !!!next-token;
5613              redo B;              next B;
5614            } else {            } else {
5615              !!!cp ('t195');              !!!cp ('t195');
5616            }            }
5617          }          }
5618    
5619              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5620    
5621              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5622              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5623                            
5624              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5625                   table => 1, tbody => 1, tfoot => 1,            # MUST
5626                   thead => 1, tr => 1,            my $foster_parent_element;
5627                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5628                # MUST            my $prev_sibling;
5629                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5630                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
5631                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5632                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5633                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5634                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5635                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5636                      !!!cp ('t196');                  $prev_sibling = $next_sibling->previous_sibling;
5637                      $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});  
5638                } else {                } else {
5639                  !!!cp ('t199');                  !!!cp ('t197');
5640                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5641                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
5642                     $next_sibling);                  #
5643                }                }
5644                  last OE;
5645                }
5646              } # OE
5647              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5648              $prev_sibling = $foster_parent_element->last_child
5649                  unless defined $foster_parent_element;
5650              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5651              if (defined $prev_sibling and
5652                  $prev_sibling->node_type == 3) {
5653                !!!cp ('t198');
5654                $prev_sibling->manakai_append_text ($token->{data});
5655              } else {
5656                !!!cp ('t199');
5657                $foster_parent_element->insert_before
5658                    ($self->{document}->create_text_node ($token->{data}),
5659                     $next_sibling);
5660              }
5661            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
5662              $open_tables->[-1]->[2] = 1; # ~node inserted
5663          } else {          } else {
5664              ## NOTE: Fragment case or in a foster parent'ed element
5665              ## (e.g. |<table><span>a|).  In fragment case, whether the
5666              ## character is appended to existing node or a new node is
5667              ## created is irrelevant, since the foster parent'ed nodes
5668              ## are discarded and fragment parsing does not invoke any
5669              ## script.
5670            !!!cp ('t200');            !!!cp ('t200');
5671            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text
5672                  ($token->{data});
5673          }          }
5674                            
5675          !!!next-token;          !!!next-token;
5676          redo B;          next B;
5677        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5678              if ({          if ({
5679                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5680                   th => 1, td => 1,               th => 1, td => 1,
5681                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5682                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5683                  ## Clear back to table context              ## Clear back to table context
5684                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5685                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5686                    !!!cp ('t201');                !!!cp ('t201');
5687                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5688                  }              }
5689                                
5690                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5691                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5692                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5693                }            }
5694              
5695                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5696                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5697                    !!!cp ('t202');                !!!cp ('t202');
5698                    !!!parse-error (type => 'missing start tag:tr');                !!!parse-error (type => 'missing start tag:tr', token => $token);
5699                  }              }
5700                                    
5701                  ## Clear back to table body context              ## Clear back to table body context
5702                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5703                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5704                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5705                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5706                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5707                    pop @{$self->{open_elements}};              }
                 }  
5708                                    
5709                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5710                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5711                    !!!cp ('t204');                !!!cp ('t204');
5712                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5713                    !!!next-token;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5714                    redo B;                !!!nack ('t204');
5715                  } else {                !!!next-token;
5716                    !!!cp ('t205');                next B;
5717                    !!!insert-element ('tr');              } else {
5718                    ## reprocess in the "in row" insertion mode                !!!cp ('t205');
5719                  }                !!!insert-element ('tr',, $token);
5720                } else {                ## reprocess in the "in row" insertion mode
5721                  !!!cp ('t206');              }
5722                }            } else {
5723                !!!cp ('t206');
5724              }
5725    
5726                ## Clear back to table row context                ## Clear back to table row context
5727                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5728                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5729                  !!!cp ('t207');                  !!!cp ('t207');
5730                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5731                }                }
5732                                
5733                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5734                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5735              $self->{insertion_mode} = IN_CELL_IM;
5736    
5737                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5738                                
5739              !!!nack ('t207.1');
5740              !!!next-token;
5741              next B;
5742            } elsif ({
5743                      caption => 1, col => 1, colgroup => 1,
5744                      tbody => 1, tfoot => 1, thead => 1,
5745                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5746                     }->{$token->{tag_name}}) {
5747              if ($self->{insertion_mode} == IN_ROW_IM) {
5748                ## As if </tr>
5749                ## have an element in table scope
5750                my $i;
5751                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5752                  my $node = $self->{open_elements}->[$_];
5753                  if ($node->[1] == TABLE_ROW_EL) {
5754                    !!!cp ('t208');
5755                    $i = $_;
5756                    last INSCOPE;
5757                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5758                    !!!cp ('t209');
5759                    last INSCOPE;
5760                  }
5761                } # INSCOPE
5762                unless (defined $i) {
5763                  !!!cp ('t210');
5764                  ## TODO: This type is wrong.
5765                  !!!parse-error (type => 'unmacthed end tag',
5766                                  text => $token->{tag_name}, token => $token);
5767                  ## Ignore the token
5768                  !!!nack ('t210.1');
5769                !!!next-token;                !!!next-token;
5770                redo B;                next B;
5771              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] eq 'tr') {  
                     !!!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;  
                 }  
5772                                    
5773                  ## Clear back to table row context                  ## Clear back to table row context
5774                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5775                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5776                    !!!cp ('t211');                    !!!cp ('t211');
5777                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
5778                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4372  sub _tree_construction_main ($) { Line 5783  sub _tree_construction_main ($) {
5783                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5784                    !!!cp ('t212');                    !!!cp ('t212');
5785                    ## reprocess                    ## reprocess
5786                    redo B;                    !!!ack-later;
5787                      next B;
5788                  } else {                  } else {
5789                    !!!cp ('t213');                    !!!cp ('t213');
5790                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4384  sub _tree_construction_main ($) { Line 5796  sub _tree_construction_main ($) {
5796                  my $i;                  my $i;
5797                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5798                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5799                    if ({                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5800                      !!!cp ('t214');                      !!!cp ('t214');
5801                      $i = $_;                      $i = $_;
5802                      last INSCOPE;                      last INSCOPE;
5803                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5804                      !!!cp ('t215');                      !!!cp ('t215');
5805                      last INSCOPE;                      last INSCOPE;
5806                    }                    }
5807                  } # INSCOPE                  } # INSCOPE
5808                  unless (defined $i) {                  unless (defined $i) {
5809                    !!!cp ('t216');                    !!!cp ('t216');
5810  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5811                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
5812                                      text => $token->{tag_name}, token => $token);
5813                    ## Ignore the token                    ## Ignore the token
5814                      !!!nack ('t216.1');
5815                    !!!next-token;                    !!!next-token;
5816                    redo B;                    next B;
5817                  }                  }
5818    
5819                  ## Clear back to table body context                  ## Clear back to table body context
5820                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5821                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5822                    !!!cp ('t217');                    !!!cp ('t217');
5823                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5824                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4429  sub _tree_construction_main ($) { Line 5838  sub _tree_construction_main ($) {
5838                  !!!cp ('t218');                  !!!cp ('t218');
5839                }                }
5840    
5841                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5842                  ## Clear back to table context              ## Clear back to table context
5843                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5844                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5845                    !!!cp ('t219');                !!!cp ('t219');
5846                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
5847                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5848                  }              }
5849                                
5850                  !!!insert-element ('colgroup');              !!!insert-element ('colgroup',, $token);
5851                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5852                  ## reprocess              ## reprocess
5853                  redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5854                } elsif ({              !!!ack-later;
5855                          caption => 1,              next B;
5856                          colgroup => 1,            } elsif ({
5857                          tbody => 1, tfoot => 1, thead => 1,                      caption => 1,
5858                         }->{$token->{tag_name}}) {                      colgroup => 1,
5859                  ## Clear back to table context                      tbody => 1, tfoot => 1, thead => 1,
5860                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                     }->{$token->{tag_name}}) {
5861                         $self->{open_elements}->[-1]->[1] ne 'html') {              ## Clear back to table context
5862                    while (not ($self->{open_elements}->[-1]->[1]
5863                                    & TABLE_SCOPING_EL)) {
5864                    !!!cp ('t220');                    !!!cp ('t220');
5865                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5866                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5867                  }                  }
5868                                    
5869                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5870                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5871                                    
5872                  !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5873                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5874                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5875                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5876                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5877                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5878                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5879                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5880                  !!!next-token;                                        }->{$token->{tag_name}};
5881                  redo B;              !!!next-token;
5882                } else {              !!!nack ('t220.1');
5883                  die "$0: in table: <>: $token->{tag_name}";              next B;
5884                }            } else {
5885                die "$0: in table: <>: $token->{tag_name}";
5886              }
5887              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5888                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5889                                  text => $self->{open_elements}->[-1]->[0]
5890                                      ->manakai_local_name,
5891                                  token => $token);
5892    
5893                ## As if </table>                ## As if </table>
5894                ## have a table element in table scope                ## have a table element in table scope
5895                my $i;                my $i;
5896                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5897                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5898                  if ($node->[1] eq 'table') {                  if ($node->[1] == TABLE_EL) {
5899                    !!!cp ('t221');                    !!!cp ('t221');
5900                    $i = $_;                    $i = $_;
5901                    last INSCOPE;                    last INSCOPE;
5902                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5903                    !!!cp ('t222');                    !!!cp ('t222');
5904                    last INSCOPE;                    last INSCOPE;
5905                  }                  }
# Line 4494  sub _tree_construction_main ($) { Line 5907  sub _tree_construction_main ($) {
5907                unless (defined $i) {                unless (defined $i) {
5908                  !!!cp ('t223');                  !!!cp ('t223');
5909  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5910                  !!!parse-error (type => 'unmatched end tag:table');                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5911                                    token => $token);
5912                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5913                    !!!nack ('t223.1');
5914                  !!!next-token;                  !!!next-token;
5915                  redo B;                  next B;
5916                }                }
5917                                
5918    ## TODO: Followings are removed from the latest spec.
5919                ## generate implied end tags                ## generate implied end tags
5920                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5921                  !!!cp ('t224');                  !!!cp ('t224');
5922                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5923                }                }
5924    
5925                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
5926                  !!!cp ('t225');                  !!!cp ('t225');
5927  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5928                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5929                                    text => $self->{open_elements}->[-1]->[0]
5930                                        ->manakai_local_name,
5931                                    token => $token);
5932                } else {                } else {
5933                  !!!cp ('t226');                  !!!cp ('t226');
5934                }                }
# Line 4521  sub _tree_construction_main ($) { Line 5938  sub _tree_construction_main ($) {
5938    
5939                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5940    
5941                ## reprocess            ## reprocess
5942                redo B;            !!!ack-later;
5943              next B;
5944          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
5945            if (not $open_tables->[-1]->[1]) { # tainted            if (not $open_tables->[-1]->[1]) { # tainted
5946              !!!cp ('t227.8');              !!!cp ('t227.8');
5947              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5948              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5949              redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5950                next B;
5951            } else {            } else {
5952              !!!cp ('t227.7');              !!!cp ('t227.7');
5953              #              #
# Line 4538  sub _tree_construction_main ($) { Line 5957  sub _tree_construction_main ($) {
5957              !!!cp ('t227.6');              !!!cp ('t227.6');
5958              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5959              $script_start_tag->();              $script_start_tag->();
5960              redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5961                next B;
5962            } else {            } else {
5963              !!!cp ('t227.5');              !!!cp ('t227.5');
5964              #              #
# Line 4549  sub _tree_construction_main ($) { Line 5969  sub _tree_construction_main ($) {
5969                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5970                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5971                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5972                  !!!parse-error (type => 'in table:'.$token->{tag_name});                  !!!parse-error (type => 'in table',
5973                                    text => $token->{tag_name}, token => $token);
5974    
5975                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5976                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5977    
5978                  ## TODO: form element pointer                  ## TODO: form element pointer
5979    
5980                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5981    
5982                  !!!next-token;                  !!!next-token;
5983                  redo B;                  !!!ack ('t227.2.1');
5984                    next B;
5985                } else {                } else {
5986                  !!!cp ('t227.2');                  !!!cp ('t227.2');
5987                  #                  #
# Line 4576  sub _tree_construction_main ($) { Line 5999  sub _tree_construction_main ($) {
5999            #            #
6000          }          }
6001    
6002          !!!parse-error (type => 'in table:'.$token->{tag_name});          !!!parse-error (type => 'in table', text => $token->{tag_name},
6003                            token => $token);
6004    
6005          $insert = $insert_to_foster;          $insert = $insert_to_foster;
6006          #          #
# Line 4587  sub _tree_construction_main ($) { Line 6011  sub _tree_construction_main ($) {
6011                my $i;                my $i;
6012                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6013                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6014                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] == TABLE_ROW_EL) {
6015                    !!!cp ('t228');                    !!!cp ('t228');
6016                    $i = $_;                    $i = $_;
6017                    last INSCOPE;                    last INSCOPE;
6018                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6019                    !!!cp ('t229');                    !!!cp ('t229');
6020                    last INSCOPE;                    last INSCOPE;
6021                  }                  }
6022                } # INSCOPE                } # INSCOPE
6023                unless (defined $i) {                unless (defined $i) {
6024                  !!!cp ('t230');                  !!!cp ('t230');
6025                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6026                                    text => $token->{tag_name}, token => $token);
6027                  ## Ignore the token                  ## Ignore the token
6028                    !!!nack ('t230.1');
6029                  !!!next-token;                  !!!next-token;
6030                  redo B;                  next B;
6031                } else {                } else {
6032                  !!!cp ('t232');                  !!!cp ('t232');
6033                }                }
6034    
6035                ## Clear back to table row context                ## Clear back to table row context
6036                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6037                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6038                  !!!cp ('t231');                  !!!cp ('t231');
6039  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6040                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4620  sub _tree_construction_main ($) { Line 6043  sub _tree_construction_main ($) {
6043                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6044                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6045                !!!next-token;                !!!next-token;
6046                redo B;                !!!nack ('t231.1');
6047                  next B;
6048              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6049                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6050                  ## As if </tr>                  ## As if </tr>
# Line 4628  sub _tree_construction_main ($) { Line 6052  sub _tree_construction_main ($) {
6052                  my $i;                  my $i;
6053                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6054                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6055                    if ($node->[1] eq 'tr') {                    if ($node->[1] == TABLE_ROW_EL) {
6056                      !!!cp ('t233');                      !!!cp ('t233');
6057                      $i = $_;                      $i = $_;
6058                      last INSCOPE;                      last INSCOPE;
6059                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6060                      !!!cp ('t234');                      !!!cp ('t234');
6061                      last INSCOPE;                      last INSCOPE;
6062                    }                    }
# Line 4642  sub _tree_construction_main ($) { Line 6064  sub _tree_construction_main ($) {
6064                  unless (defined $i) {                  unless (defined $i) {
6065                    !!!cp ('t235');                    !!!cp ('t235');
6066  ## TODO: The following is wrong.  ## TODO: The following is wrong.
6067                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!parse-error (type => 'unmatched end tag',
6068                                      text => $token->{type}, token => $token);
6069                    ## Ignore the token                    ## Ignore the token
6070                      !!!nack ('t236.1');
6071                    !!!next-token;                    !!!next-token;
6072                    redo B;                    next B;
6073                  }                  }
6074                                    
6075                  ## Clear back to table row context                  ## Clear back to table row context
6076                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6077                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6078                    !!!cp ('t236');                    !!!cp ('t236');
6079  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6080                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4667  sub _tree_construction_main ($) { Line 6090  sub _tree_construction_main ($) {
6090                  my $i;                  my $i;
6091                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6092                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6093                    if ({                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
6094                      !!!cp ('t237');                      !!!cp ('t237');
6095                      $i = $_;                      $i = $_;
6096                      last INSCOPE;                      last INSCOPE;
6097                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6098                      !!!cp ('t238');                      !!!cp ('t238');
6099                      last INSCOPE;                      last INSCOPE;
6100                    }                    }
6101                  } # INSCOPE                  } # INSCOPE
6102                  unless (defined $i) {                  unless (defined $i) {
6103                    !!!cp ('t239');                    !!!cp ('t239');
6104                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
6105                                      text => $token->{tag_name}, token => $token);
6106                    ## Ignore the token                    ## Ignore the token
6107                      !!!nack ('t239.1');
6108                    !!!next-token;                    !!!next-token;
6109                    redo B;                    next B;
6110                  }                  }
6111                                    
6112                  ## Clear back to table body context                  ## Clear back to table body context
6113                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6114                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6115                    !!!cp ('t240');                    !!!cp ('t240');
6116                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6117                  }                  }
# Line 4717  sub _tree_construction_main ($) { Line 6137  sub _tree_construction_main ($) {
6137                my $i;                my $i;
6138                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6139                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6140                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] == TABLE_EL) {
6141                    !!!cp ('t241');                    !!!cp ('t241');
6142                    $i = $_;                    $i = $_;
6143                    last INSCOPE;                    last INSCOPE;
6144                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6145                    !!!cp ('t242');                    !!!cp ('t242');
6146                    last INSCOPE;                    last INSCOPE;
6147                  }                  }
6148                } # INSCOPE                } # INSCOPE
6149                unless (defined $i) {                unless (defined $i) {
6150                  !!!cp ('t243');                  !!!cp ('t243');
6151                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6152                                    text => $token->{tag_name}, token => $token);
6153                  ## Ignore the token                  ## Ignore the token
6154                    !!!nack ('t243.1');
6155                  !!!next-token;                  !!!next-token;
6156                  redo B;                  next B;
6157                }                }
6158                                    
6159                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 4742  sub _tree_construction_main ($) { Line 6162  sub _tree_construction_main ($) {
6162                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6163                                
6164                !!!next-token;                !!!next-token;
6165                redo B;                next B;
6166              } elsif ({              } elsif ({
6167                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6168                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4752  sub _tree_construction_main ($) { Line 6172  sub _tree_construction_main ($) {
6172                  my $i;                  my $i;
6173                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6174                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6175                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6176                      !!!cp ('t247');                      !!!cp ('t247');
6177                      $i = $_;                      $i = $_;
6178                      last INSCOPE;                      last INSCOPE;
6179                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6180                      !!!cp ('t248');                      !!!cp ('t248');
6181                      last INSCOPE;                      last INSCOPE;
6182                    }                    }
6183                  } # INSCOPE                  } # INSCOPE
6184                    unless (defined $i) {                    unless (defined $i) {
6185                      !!!cp ('t249');                      !!!cp ('t249');
6186                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
6187                                        text => $token->{tag_name}, token => $token);
6188                      ## Ignore the token                      ## Ignore the token
6189                        !!!nack ('t249.1');
6190                      !!!next-token;                      !!!next-token;
6191                      redo B;                      next B;
6192                    }                    }
6193                                    
6194                  ## As if </tr>                  ## As if </tr>
# Line 4776  sub _tree_construction_main ($) { Line 6196  sub _tree_construction_main ($) {
6196                  my $i;                  my $i;
6197                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6198                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6199                    if ($node->[1] eq 'tr') {                    if ($node->[1] == TABLE_ROW_EL) {
6200                      !!!cp ('t250');                      !!!cp ('t250');
6201                      $i = $_;                      $i = $_;
6202                      last INSCOPE;                      last INSCOPE;
6203                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6204                      !!!cp ('t251');                      !!!cp ('t251');
6205                      last INSCOPE;                      last INSCOPE;
6206                    }                    }
6207                  } # INSCOPE                  } # INSCOPE
6208                    unless (defined $i) {                    unless (defined $i) {
6209                      !!!cp ('t252');                      !!!cp ('t252');
6210                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!parse-error (type => 'unmatched end tag',
6211                                        text => 'tr', token => $token);
6212                      ## Ignore the token                      ## Ignore the token
6213                        !!!nack ('t252.1');
6214                      !!!next-token;                      !!!next-token;
6215                      redo B;                      next B;
6216                    }                    }
6217                                    
6218                  ## Clear back to table row context                  ## Clear back to table row context
6219                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6220                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6221                    !!!cp ('t253');                    !!!cp ('t253');
6222  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6223                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4813  sub _tree_construction_main ($) { Line 6232  sub _tree_construction_main ($) {
6232                my $i;                my $i;
6233                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6234                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6235                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6236                    !!!cp ('t254');                    !!!cp ('t254');
6237                    $i = $_;                    $i = $_;
6238                    last INSCOPE;                    last INSCOPE;
6239                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6240                    !!!cp ('t255');                    !!!cp ('t255');
6241                    last INSCOPE;                    last INSCOPE;
6242                  }                  }
6243                } # INSCOPE                } # INSCOPE
6244                unless (defined $i) {                unless (defined $i) {
6245                  !!!cp ('t256');                  !!!cp ('t256');
6246                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6247                                    text => $token->{tag_name}, token => $token);
6248                  ## Ignore the token                  ## Ignore the token
6249                    !!!nack ('t256.1');
6250                  !!!next-token;                  !!!next-token;
6251                  redo B;                  next B;
6252                }                }
6253    
6254                ## Clear back to table body context                ## Clear back to table body context
6255                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6256                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6257                  !!!cp ('t257');                  !!!cp ('t257');
6258  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6259                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4843  sub _tree_construction_main ($) { Line 6261  sub _tree_construction_main ($) {
6261    
6262                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6263                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6264                  !!!nack ('t257.1');
6265                !!!next-token;                !!!next-token;
6266                redo B;                next B;
6267              } elsif ({              } elsif ({
6268                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6269                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6270                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6271                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6272                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6273                !!!cp ('t258');            !!!cp ('t258');
6274                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6275                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6276                !!!next-token;            ## Ignore the token
6277                redo B;            !!!nack ('t258.1');
6278               !!!next-token;
6279              next B;
6280          } else {          } else {
6281            !!!cp ('t259');            !!!cp ('t259');
6282            !!!parse-error (type => 'in table:/'.$token->{tag_name});            !!!parse-error (type => 'in table:/',
6283                              text => $token->{tag_name}, token => $token);
6284    
6285            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6286            #            #
6287          }          }
6288          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6289            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6290                    @{$self->{open_elements}} == 1) { # redundant, maybe
6291              !!!parse-error (type => 'in body:#eof', token => $token);
6292              !!!cp ('t259.1');
6293              #
6294            } else {
6295              !!!cp ('t259.2');
6296              #
6297            }
6298    
6299            ## Stop parsing
6300            last B;
6301        } else {        } else {
6302          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6303        }        }
6304      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6305            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6306              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6307                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6308                unless (length $token->{data}) {                unless (length $token->{data}) {
6309                  !!!cp ('t260');                  !!!cp ('t260');
6310                  !!!next-token;                  !!!next-token;
6311                  redo B;                  next B;
6312                }                }
6313              }              }
6314                            
# Line 4882  sub _tree_construction_main ($) { Line 6317  sub _tree_construction_main ($) {
6317            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6318              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6319                !!!cp ('t262');                !!!cp ('t262');
6320                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6321                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6322                  !!!ack ('t262.1');
6323                !!!next-token;                !!!next-token;
6324                redo B;                next B;
6325              } else {              } else {
6326                !!!cp ('t263');                !!!cp ('t263');
6327                #                #
6328              }              }
6329            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6330              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6331                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6332                  !!!cp ('t264');                  !!!cp ('t264');
6333                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!parse-error (type => 'unmatched end tag',
6334                                    text => 'colgroup', token => $token);
6335                  ## Ignore the token                  ## Ignore the token
6336                  !!!next-token;                  !!!next-token;
6337                  redo B;                  next B;
6338                } else {                } else {
6339                  !!!cp ('t265');                  !!!cp ('t265');
6340                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6341                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6342                  !!!next-token;                  !!!next-token;
6343                  redo B;                              next B;            
6344                }                }
6345              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6346                !!!cp ('t266');                !!!cp ('t266');
6347                !!!parse-error (type => 'unmatched end tag:col');                !!!parse-error (type => 'unmatched end tag',
6348                                  text => 'col', token => $token);
6349                ## Ignore the token                ## Ignore the token
6350                !!!next-token;                !!!next-token;
6351                redo B;                next B;
6352              } else {              } else {
6353                !!!cp ('t267');                !!!cp ('t267');
6354                #                #
6355              }              }
6356            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6357              die "$0: $token->{type}: Unknown token type";          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6358            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6359              !!!cp ('t270.2');
6360              ## Stop parsing.
6361              last B;
6362            } else {
6363              ## NOTE: As if </colgroup>.
6364              !!!cp ('t270.1');
6365              pop @{$self->{open_elements}}; # colgroup
6366              $self->{insertion_mode} = IN_TABLE_IM;
6367              ## Reprocess.
6368              next B;
6369            }
6370          } else {
6371            die "$0: $token->{type}: Unknown token type";
6372          }
6373    
6374            ## As if </colgroup>            ## As if </colgroup>
6375            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6376              !!!cp ('t269');              !!!cp ('t269');
6377              !!!parse-error (type => 'unmatched end tag:colgroup');  ## TODO: Wrong error type?
6378                !!!parse-error (type => 'unmatched end tag',
6379                                text => 'colgroup', token => $token);
6380              ## Ignore the token              ## Ignore the token
6381                !!!nack ('t269.1');
6382              !!!next-token;              !!!next-token;
6383              redo B;              next B;
6384            } else {            } else {
6385              !!!cp ('t270');              !!!cp ('t270');
6386              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6387              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6388                !!!ack-later;
6389              ## reprocess              ## reprocess
6390              redo B;              next B;
6391            }            }
6392      } elsif ($self->{insertion_mode} & SELECT_IMS) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6393        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6394          !!!cp ('t271');          !!!cp ('t271');
6395          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6396          !!!next-token;          !!!next-token;
6397          redo B;          next B;
6398        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6399              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6400                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6401                  !!!cp ('t272');              !!!cp ('t272');
6402                  ## As if </option>              ## As if </option>
6403                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6404                } else {            } else {
6405                  !!!cp ('t273');              !!!cp ('t273');
6406                }            }
6407    
6408                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6409                !!!next-token;            !!!nack ('t273.1');
6410                redo B;            !!!next-token;
6411              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6412                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6413                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6414                  ## As if </option>              !!!cp ('t274');
6415                  pop @{$self->{open_elements}};              ## As if </option>
6416                } else {              pop @{$self->{open_elements}};
6417                  !!!cp ('t275');            } else {
6418                }              !!!cp ('t275');
6419              }
6420    
6421                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6422                  !!!cp ('t276');              !!!cp ('t276');
6423                  ## As if </optgroup>              ## As if </optgroup>
6424                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6425                } else {            } else {
6426                  !!!cp ('t277');              !!!cp ('t277');
6427                }            }
6428    
6429                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6430                !!!next-token;            !!!nack ('t277.1');
6431                redo B;            !!!next-token;
6432          } elsif ($token->{tag_name} eq 'select' or            next B;
6433                   $token->{tag_name} eq 'input' or          } elsif ({
6434                       select => 1, input => 1, textarea => 1,
6435                     }->{$token->{tag_name}} or
6436                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6437                    {                    {
6438                     caption => 1, table => 1,                     caption => 1, table => 1,
# Line 4981  sub _tree_construction_main ($) { Line 6440  sub _tree_construction_main ($) {
6440                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
6441                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
6442            ## TODO: The type below is not good - <select> is replaced by </select>            ## TODO: The type below is not good - <select> is replaced by </select>
6443            !!!parse-error (type => 'not closed:select');            !!!parse-error (type => 'not closed', text => 'select',
6444                              token => $token);
6445            ## NOTE: As if the token were </select> (<select> case) or            ## NOTE: As if the token were </select> (<select> case) or
6446            ## as if there were </select> (otherwise).            ## as if there were </select> (otherwise).
6447                ## have an element in table scope            ## have an element in table scope
6448                my $i;            my $i;
6449                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6450                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6451                  if ($node->[1] eq 'select') {              if ($node->[1] == SELECT_EL) {
6452                    !!!cp ('t278');                !!!cp ('t278');
6453                    $i = $_;                $i = $_;
6454                    last INSCOPE;                last INSCOPE;
6455                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6456                            table => 1, html => 1,                !!!cp ('t279');
6457                           }->{$node->[1]}) {                last INSCOPE;
6458                    !!!cp ('t279');              }
6459                    last INSCOPE;            } # INSCOPE
6460                  }            unless (defined $i) {
6461                } # INSCOPE              !!!cp ('t280');
6462                unless (defined $i) {              !!!parse-error (type => 'unmatched end tag',
6463                  !!!cp ('t280');                              text => 'select', token => $token);
6464                  !!!parse-error (type => 'unmatched end tag:select');              ## Ignore the token
6465                  ## Ignore the token              !!!nack ('t280.1');
6466                  !!!next-token;              !!!next-token;
6467                  redo B;              next B;
6468                }            }
6469                                
6470                !!!cp ('t281');            !!!cp ('t281');
6471                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6472    
6473                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6474    
6475            if ($token->{tag_name} eq 'select') {            if ($token->{tag_name} eq 'select') {
6476              !!!cp ('t281.2');              !!!nack ('t281.2');
6477              !!!next-token;              !!!next-token;
6478              redo B;              next B;
6479            } else {            } else {
6480              !!!cp ('t281.1');              !!!cp ('t281.1');
6481                !!!ack-later;
6482              ## Reprocess the token.              ## Reprocess the token.
6483              redo B;              next B;
6484            }            }
6485          } else {          } else {
6486            !!!cp ('t282');            !!!cp ('t282');
6487            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!parse-error (type => 'in select',
6488                              text => $token->{tag_name}, token => $token);
6489            ## Ignore the token            ## Ignore the token
6490              !!!nack ('t282.1');
6491            !!!next-token;            !!!next-token;
6492            redo B;            next B;
6493          }          }
6494        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6495              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6496                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
6497                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
6498                  !!!cp ('t283');              !!!cp ('t283');
6499                  ## As if </option>              ## As if </option>
6500                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6501                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6502                  !!!cp ('t284');              !!!cp ('t284');
6503                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6504                } else {            } else {
6505                  !!!cp ('t285');              !!!cp ('t285');
6506                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6507                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6508                }              ## Ignore the token
6509                !!!next-token;            }
6510                redo B;            !!!nack ('t285.1');
6511              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6512                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6513                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6514                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6515                } else {              !!!cp ('t286');
6516                  !!!cp ('t287');              pop @{$self->{open_elements}};
6517                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            } else {
6518                  ## Ignore the token              !!!cp ('t287');
6519                }              !!!parse-error (type => 'unmatched end tag',
6520                !!!next-token;                              text => $token->{tag_name}, token => $token);
6521                redo B;              ## Ignore the token
6522              } elsif ($token->{tag_name} eq 'select') {            }
6523                ## have an element in table scope            !!!nack ('t287.1');
6524                my $i;            !!!next-token;
6525                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6526                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6527                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6528                    !!!cp ('t288');            my $i;
6529                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6530                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6531                  } elsif ({              if ($node->[1] == SELECT_EL) {
6532                            table => 1, html => 1,                !!!cp ('t288');
6533                           }->{$node->[1]}) {                $i = $_;
6534                    !!!cp ('t289');                last INSCOPE;
6535                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6536                  }                !!!cp ('t289');
6537                } # INSCOPE                last INSCOPE;
6538                unless (defined $i) {              }
6539                  !!!cp ('t290');            } # INSCOPE
6540                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            unless (defined $i) {
6541                  ## Ignore the token              !!!cp ('t290');
6542                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6543                  redo B;                              text => $token->{tag_name}, token => $token);
6544                }              ## Ignore the token
6545                !!!nack ('t290.1');
6546                !!!next-token;
6547                next B;
6548              }
6549                                
6550                !!!cp ('t291');            !!!cp ('t291');
6551                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6552    
6553                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6554    
6555                !!!next-token;            !!!nack ('t291.1');
6556                redo B;            !!!next-token;
6557              next B;
6558          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6559                   {                   {
6560                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
6561                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6562                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
6563  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6564                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6565                              text => $token->{tag_name}, token => $token);
6566                                
6567                ## have an element in table scope            ## have an element in table scope
6568                my $i;            my $i;
6569                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6570                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6571                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6572                    !!!cp ('t292');                !!!cp ('t292');
6573                    $i = $_;                $i = $_;
6574                    last INSCOPE;                last INSCOPE;
6575                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6576                            table => 1, html => 1,                !!!cp ('t293');
6577                           }->{$node->[1]}) {                last INSCOPE;
6578                    !!!cp ('t293');              }
6579                    last INSCOPE;            } # INSCOPE
6580                  }            unless (defined $i) {
6581                } # INSCOPE              !!!cp ('t294');
6582                unless (defined $i) {              ## Ignore the token
6583                  !!!cp ('t294');              !!!nack ('t294.1');
6584                  ## Ignore the token              !!!next-token;
6585                  !!!next-token;              next B;
6586                  redo B;            }
               }  
6587                                
6588                ## As if </select>            ## As if </select>
6589                ## have an element in table scope            ## have an element in table scope
6590                undef $i;            undef $i;
6591                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6592                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6593                  if ($node->[1] eq 'select') {              if ($node->[1] == SELECT_EL) {
6594                    !!!cp ('t295');                !!!cp ('t295');
6595                    $i = $_;                $i = $_;
6596                    last INSCOPE;                last INSCOPE;
6597                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6598  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6599                    !!!cp ('t296');                !!!cp ('t296');
6600                    last INSCOPE;                last INSCOPE;
6601                  }              }
6602                } # INSCOPE            } # INSCOPE
6603                unless (defined $i) {            unless (defined $i) {
6604                  !!!cp ('t297');              !!!cp ('t297');
6605  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6606                  !!!parse-error (type => 'unmatched end tag:select');              !!!parse-error (type => 'unmatched end tag',
6607                  ## Ignore the </select> token                              text => 'select', token => $token);
6608                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6609                  redo B;              !!!nack ('t297.1');
6610                }              !!!next-token; ## TODO: ok?
6611                next B;
6612              }
6613                                
6614                !!!cp ('t298');            !!!cp ('t298');
6615                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6616    
6617                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6618    
6619                ## reprocess            !!!ack-later;
6620                redo B;            ## reprocess
6621              next B;
6622          } else {          } else {
6623            !!!cp ('t299');            !!!cp ('t299');
6624            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!parse-error (type => 'in select:/',
6625                              text => $token->{tag_name}, token => $token);
6626            ## Ignore the token            ## Ignore the token
6627              !!!nack ('t299.3');
6628            !!!next-token;            !!!next-token;
6629            redo B;            next B;
6630            }
6631          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6632            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6633                    @{$self->{open_elements}} == 1) { # redundant, maybe
6634              !!!cp ('t299.1');
6635              !!!parse-error (type => 'in body:#eof', token => $token);
6636            } else {
6637              !!!cp ('t299.2');
6638          }          }
6639    
6640            ## Stop parsing.
6641            last B;
6642        } else {        } else {
6643          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6644        }        }
6645      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6646        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6647          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6648            my $data = $1;            my $data = $1;
6649            ## As if in body            ## As if in body
6650            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5172  sub _tree_construction_main ($) { Line 6654  sub _tree_construction_main ($) {
6654            unless (length $token->{data}) {            unless (length $token->{data}) {
6655              !!!cp ('t300');              !!!cp ('t300');
6656              !!!next-token;              !!!next-token;
6657              redo B;              next B;
6658            }            }
6659          }          }
6660                    
6661          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6662            !!!cp ('t301');            !!!cp ('t301');
6663            !!!parse-error (type => 'after html:#character');            !!!parse-error (type => 'after html:#text', token => $token);
6664              #
           ## Reprocess in the "after body" insertion mode.  
6665          } else {          } else {
6666            !!!cp ('t302');            !!!cp ('t302');
6667              ## "after body" insertion mode
6668              !!!parse-error (type => 'after body:#text', token => $token);
6669              #
6670          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6671    
6672          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6673          ## reprocess          ## reprocess
6674          redo B;          next B;
6675        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6676          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6677            !!!cp ('t303');            !!!cp ('t303');
6678            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html',
6679                                        text => $token->{tag_name}, token => $token);
6680            ## Reprocess in the "after body" insertion mode.            #
6681          } else {          } else {
6682            !!!cp ('t304');            !!!cp ('t304');
6683              ## "after body" insertion mode
6684              !!!parse-error (type => 'after body',
6685                              text => $token->{tag_name}, token => $token);
6686              #
6687          }          }
6688    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6689          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6690            !!!ack-later;
6691          ## reprocess          ## reprocess
6692          redo B;          next B;
6693        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6694          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6695            !!!cp ('t305');            !!!cp ('t305');
6696            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/',
6697                              text => $token->{tag_name}, token => $token);
6698                        
6699            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6700            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6701              next B;
6702          } else {          } else {
6703            !!!cp ('t306');            !!!cp ('t306');
6704          }          }
# Line 5222  sub _tree_construction_main ($) { Line 6707  sub _tree_construction_main ($) {
6707          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6708            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6709              !!!cp ('t307');              !!!cp ('t307');
6710              !!!parse-error (type => 'unmatched end tag:html');              !!!parse-error (type => 'unmatched end tag',
6711                                text => 'html', token => $token);
6712              ## Ignore the token              ## Ignore the token
6713              !!!next-token;              !!!next-token;
6714              redo B;              next B;
6715            } else {            } else {
6716              !!!cp ('t308');              !!!cp ('t308');
6717              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6718              !!!next-token;              !!!next-token;
6719              redo B;              next B;
6720            }            }
6721          } else {          } else {
6722            !!!cp ('t309');            !!!cp ('t309');
6723            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!parse-error (type => 'after body:/',
6724                              text => $token->{tag_name}, token => $token);
6725    
6726            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6727            ## reprocess            ## reprocess
6728            redo B;            next B;
6729          }          }
6730          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6731            !!!cp ('t309.2');
6732            ## Stop parsing
6733            last B;
6734        } else {        } else {
6735          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6736        }        }
6737      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6738        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6739          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6740            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6741                        
6742            unless (length $token->{data}) {            unless (length $token->{data}) {
6743              !!!cp ('t310');              !!!cp ('t310');
6744              !!!next-token;              !!!next-token;
6745              redo B;              next B;
6746            }            }
6747          }          }
6748                    
6749          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6750            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6751              !!!cp ('t311');              !!!cp ('t311');
6752              !!!parse-error (type => 'in frameset:#character');              !!!parse-error (type => 'in frameset:#text', token => $token);
6753            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6754              !!!cp ('t312');              !!!cp ('t312');
6755              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#text', token => $token);
6756            } else { # "after html frameset"            } else { # "after after frameset"
6757              !!!cp ('t313');              !!!cp ('t313');
6758              !!!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');  
6759            }            }
6760                        
6761            ## Ignore the token.            ## Ignore the token.
# Line 5279  sub _tree_construction_main ($) { Line 6766  sub _tree_construction_main ($) {
6766              !!!cp ('t315');              !!!cp ('t315');
6767              !!!next-token;              !!!next-token;
6768            }            }
6769            redo B;            next B;
6770          }          }
6771                    
6772          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6773        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!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');  
         }  
   
6774          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6775              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6776            !!!cp ('t318');            !!!cp ('t318');
6777            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6778              !!!nack ('t318.1');
6779            !!!next-token;            !!!next-token;
6780            redo B;            next B;
6781          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6782                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6783            !!!cp ('t319');            !!!cp ('t319');
6784            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6785            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6786              !!!ack ('t319.1');
6787            !!!next-token;            !!!next-token;
6788            redo B;            next B;
6789          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6790            !!!cp ('t320');            !!!cp ('t320');
6791            ## NOTE: As if in body.            ## NOTE: As if in head.
6792            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6793            redo B;            next B;
6794    
6795              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6796              ## has no parse error.
6797          } else {          } else {
6798            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6799              !!!cp ('t321');              !!!cp ('t321');
6800              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!parse-error (type => 'in frameset',
6801            } else {                              text => $token->{tag_name}, token => $token);
6802              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6803              !!!cp ('t322');              !!!cp ('t322');
6804              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!parse-error (type => 'after frameset',
6805                                text => $token->{tag_name}, token => $token);
6806              } else { # "after after frameset"
6807                !!!cp ('t322.2');
6808                !!!parse-error (type => 'after after frameset',
6809                                text => $token->{tag_name}, token => $token);
6810            }            }
6811            ## Ignore the token            ## Ignore the token
6812              !!!nack ('t322.1');
6813            !!!next-token;            !!!next-token;
6814            redo B;            next B;
6815          }          }
6816        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!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');  
         }  
   
6817          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6818              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6819            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6820                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6821              !!!cp ('t325');              !!!cp ('t325');
6822              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6823                                text => $token->{tag_name}, token => $token);
6824              ## Ignore the token              ## Ignore the token
6825              !!!next-token;              !!!next-token;
6826            } else {            } else {
# Line 5350  sub _tree_construction_main ($) { Line 6830  sub _tree_construction_main ($) {
6830            }            }
6831    
6832            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6833                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
6834              !!!cp ('t327');              !!!cp ('t327');
6835              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6836            } else {            } else {
6837              !!!cp ('t328');              !!!cp ('t328');
6838            }            }
6839            redo B;            next B;
6840          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6841                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6842            !!!cp ('t329');            !!!cp ('t329');
6843            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6844            !!!next-token;            !!!next-token;
6845            redo B;            next B;
6846          } else {          } else {
6847            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6848              !!!cp ('t330');              !!!cp ('t330');
6849              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!parse-error (type => 'in frameset:/',
6850            } else {                              text => $token->{tag_name}, token => $token);
6851              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6852                !!!cp ('t330.1');
6853                !!!parse-error (type => 'after frameset:/',
6854                                text => $token->{tag_name}, token => $token);
6855              } else { # "after after html"
6856              !!!cp ('t331');              !!!cp ('t331');
6857              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!parse-error (type => 'after after frameset:/',
6858                                text => $token->{tag_name}, token => $token);
6859            }            }
6860            ## Ignore the token            ## Ignore the token
6861            !!!next-token;            !!!next-token;
6862            redo B;            next B;
6863            }
6864          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6865            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6866                    @{$self->{open_elements}} == 1) { # redundant, maybe
6867              !!!cp ('t331.1');
6868              !!!parse-error (type => 'in body:#eof', token => $token);
6869            } else {
6870              !!!cp ('t331.2');
6871          }          }
6872            
6873            ## Stop parsing
6874            last B;
6875        } else {        } else {
6876          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6877        }        }
   
       ## ISSUE: An issue in spec here  
6878      } else {      } else {
6879        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6880      }      }
# Line 5390  sub _tree_construction_main ($) { Line 6885  sub _tree_construction_main ($) {
6885          !!!cp ('t332');          !!!cp ('t332');
6886          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6887          $script_start_tag->();          $script_start_tag->();
6888          redo B;          next B;
6889        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6890          !!!cp ('t333');          !!!cp ('t333');
6891          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6892          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6893          redo B;          next B;
6894        } elsif ({        } elsif ({
6895                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6896                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6897          !!!cp ('t334');          !!!cp ('t334');
6898          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6899          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6900          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6901            !!!ack ('t334.1');
6902          !!!next-token;          !!!next-token;
6903          redo B;          next B;
6904        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6905          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6906          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6907          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6908    
6909          unless ($self->{confident}) {          unless ($self->{confident}) {
6910            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6911              !!!cp ('t335');              !!!cp ('t335');
6912                ## NOTE: Whether the encoding is supported or not is handled
6913                ## in the {change_encoding} callback.
6914              $self->{change_encoding}              $self->{change_encoding}
6915                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6916                            
6917              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6918                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6919                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6920                                           ->{has_reference});                                           ->{has_reference});
6921            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6922              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6923                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6924                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6925                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6926                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6927                       /x) {
6928                !!!cp ('t336');                !!!cp ('t336');
6929                  ## NOTE: Whether the encoding is supported or not is handled
6930                  ## in the {change_encoding} callback.
6931                $self->{change_encoding}                $self->{change_encoding}
6932                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6933                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6934                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6935                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 5453  sub _tree_construction_main ($) { Line 6953  sub _tree_construction_main ($) {
6953            }            }
6954          }          }
6955    
6956            !!!ack ('t338.1');
6957          !!!next-token;          !!!next-token;
6958          redo B;          next B;
6959        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6960          !!!cp ('t341');          !!!cp ('t341');
6961          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6962          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6963          redo B;          next B;
6964        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6965          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6966                                
6967          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6968              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
6969            !!!cp ('t342');            !!!cp ('t342');
6970            ## Ignore the token            ## Ignore the token
6971          } else {          } else {
# Line 5478  sub _tree_construction_main ($) { Line 6979  sub _tree_construction_main ($) {
6979              }              }
6980            }            }
6981          }          }
6982            !!!nack ('t343.1');
6983          !!!next-token;          !!!next-token;
6984          redo B;          next B;
6985        } elsif ({        } elsif ({
6986                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6987                  div => 1, dl => 1, fieldset => 1,  
6988                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6989                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6990                    center => 1, datagrid => 1, details => 1, dialog => 1,
6991                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6992                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6993                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6994                    section => 1, ul => 1,
6995                    ## NOTE: As normal, but drops leading newline
6996                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6997                    ## NOTE: As normal, but interacts with the form element pointer
6998                    form => 1,
6999                    
7000                    table => 1,
7001                    hr => 1,
7002                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7003            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
7004              !!!cp ('t350');
7005              !!!parse-error (type => 'in form:form', token => $token);
7006              ## Ignore the token
7007              !!!nack ('t350.1');
7008              !!!next-token;
7009              next B;
7010            }
7011    
7012          ## has a p element in scope          ## has a p element in scope
7013          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7014            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7015              !!!cp ('t344');              !!!cp ('t344');
7016              !!!back-token;              !!!back-token; # <form>
7017              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7018              redo B;                        line => $token->{line}, column => $token->{column}};
7019            } elsif ({              next B;
7020                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7021              !!!cp ('t345');              !!!cp ('t345');
7022              last INSCOPE;              last INSCOPE;
7023            }            }
7024          } # INSCOPE          } # INSCOPE
7025                        
7026          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7027          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
7028              !!!nack ('t346.1');
7029            !!!next-token;            !!!next-token;
7030            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
7031              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5517  sub _tree_construction_main ($) { Line 7038  sub _tree_construction_main ($) {
7038            } else {            } else {
7039              !!!cp ('t348');              !!!cp ('t348');
7040            }            }
7041          } else {          } elsif ($token->{tag_name} eq 'form') {
7042            !!!cp ('t347');            !!!cp ('t347.1');
7043              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7044    
7045              !!!nack ('t347.2');
7046            !!!next-token;            !!!next-token;
7047          }          } elsif ($token->{tag_name} eq 'table') {
7048          redo B;            !!!cp ('t382');
7049        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7050          if (defined $self->{form_element}) {            
7051            !!!cp ('t350');            $self->{insertion_mode} = IN_TABLE_IM;
7052            !!!parse-error (type => 'in form:form');  
7053            ## Ignore the token            !!!nack ('t382.1');
7054              !!!next-token;
7055            } elsif ($token->{tag_name} eq 'hr') {
7056              !!!cp ('t386');
7057              pop @{$self->{open_elements}};
7058            
7059              !!!nack ('t386.1');
7060            !!!next-token;            !!!next-token;
           redo B;  
7061          } else {          } else {
7062            ## has a p element in scope            !!!nack ('t347.1');
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!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];  
7063            !!!next-token;            !!!next-token;
           redo B;  
7064          }          }
7065            next B;
7066        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
7067          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7068          INSCOPE: for (reverse @{$self->{open_elements}}) {  
7069            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7070              !!!cp ('t353');            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7071              !!!back-token;            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7072              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7073              redo B;            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7074            } elsif ({            ## object (Fx)
7075                      table => 1, caption => 1, td => 1, th => 1,            ## Generate non-tree (non-conforming)
7076                      button => 1, marquee => 1, object => 1, html => 1,            ## basefont (IE7 (where basefont is non-void)), center (IE),
7077                     }->{$_->[1]}) {            ## form (IE), hn (IE)
7078              !!!cp ('t354');          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7079              last INSCOPE;            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7080            }            ## div (Fx, S)
7081          } # INSCOPE  
7082                      my $non_optional;
         ## Step 1  
7083          my $i = -1;          my $i = -1;
7084          my $node = $self->{open_elements}->[$i];  
7085          LI: {          ## 1.
7086            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7087            if ($node->[1] eq 'li') {            if ($node->[1] == LI_EL) {
7088              if ($i != -1) {              ## 2. (a) As if </li>
7089                !!!cp ('t355');              {
7090                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7091                                $self->{open_elements}->[-1]->[1]);                #
7092              } else {  
7093                !!!cp ('t356');                ## Otherwise
7094    
7095                  ## 1. generate implied end tags, except for </li>
7096                  #
7097    
7098                  ## 2. If current node != "li", parse error
7099                  if ($non_optional) {
7100                    !!!parse-error (type => 'not closed',
7101                                    text => $non_optional->[0]->manakai_local_name,
7102                                    token => $token);
7103                    !!!cp ('t355');
7104                  } else {
7105                    !!!cp ('t356');
7106                  }
7107    
7108                  ## 3. Pop
7109                  splice @{$self->{open_elements}}, $i;
7110              }              }
7111              splice @{$self->{open_elements}}, $i;  
7112              last LI;              last; ## 2. (b) goto 5.
7113            } else {            } elsif (
7114                       ## NOTE: not "formatting" and not "phrasing"
7115                       ($node->[1] & SPECIAL_EL or
7116                        $node->[1] & SCOPING_EL) and
7117                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7118                       (not $node->[1] & ADDRESS_DIV_P_EL)
7119                      ) {
7120                ## 3.
7121              !!!cp ('t357');              !!!cp ('t357');
7122            }              last; ## goto 5.
7123                        } 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') {  
7124              !!!cp ('t358');              !!!cp ('t358');
7125              last LI;              #
7126              } else {
7127                !!!cp ('t359');
7128                $non_optional ||= $node;
7129                #
7130            }            }
7131                        ## 4.
7132            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7133            $i--;            $i--;
7134            $node = $self->{open_elements}->[$i];          }
7135            redo LI;  
7136          } # LI          ## 5. (a) has a |p| element in scope
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
7137          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7138            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7139              !!!cp ('t360');              !!!cp ('t353');
7140              !!!back-token;  
7141              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              ## NOTE: |<p><li>|, for example.
7142              redo B;  
7143            } elsif ({              !!!back-token; # <x>
7144                      table => 1, caption => 1, td => 1, th => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7145                      button => 1, marquee => 1, object => 1, html => 1,                        line => $token->{line}, column => $token->{column}};
7146                     }->{$_->[1]}) {              next B;
7147              !!!cp ('t361');            } elsif ($_->[1] & SCOPING_EL) {
7148                !!!cp ('t354');
7149              last INSCOPE;              last INSCOPE;
7150            }            }
7151          } # INSCOPE          } # INSCOPE
7152              
7153          ## Step 1          ## 5. (b) insert
7154            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7155            !!!nack ('t359.1');
7156            !!!next-token;
7157            next B;
7158          } elsif ($token->{tag_name} eq 'dt' or
7159                   $token->{tag_name} eq 'dd') {
7160            ## NOTE: As normal, but imply </dt> or </dd> when ...
7161    
7162            my $non_optional;
7163          my $i = -1;          my $i = -1;
7164          my $node = $self->{open_elements}->[$i];  
7165          LI: {          ## 1.
7166            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7167            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] == DT_EL or $node->[1] == DD_EL) {
7168              if ($i != -1) {              ## 2. (a) As if </li>
7169                !!!cp ('t362');              {
7170                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7171                                $self->{open_elements}->[-1]->[1]);                #
7172              } else {  
7173                !!!cp ('t363');                ## Otherwise
7174    
7175                  ## 1. generate implied end tags, except for </dt> or </dd>
7176                  #
7177    
7178                  ## 2. If current node != "dt"|"dd", parse error
7179                  if ($non_optional) {
7180                    !!!parse-error (type => 'not closed',
7181                                    text => $non_optional->[0]->manakai_local_name,
7182                                    token => $token);
7183                    !!!cp ('t355.1');
7184                  } else {
7185                    !!!cp ('t356.1');
7186                  }
7187    
7188                  ## 3. Pop
7189                  splice @{$self->{open_elements}}, $i;
7190              }              }
7191              splice @{$self->{open_elements}}, $i;  
7192              last LI;              last; ## 2. (b) goto 5.
7193              } elsif (
7194                       ## NOTE: not "formatting" and not "phrasing"
7195                       ($node->[1] & SPECIAL_EL or
7196                        $node->[1] & SCOPING_EL) and
7197                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7198    
7199                       (not $node->[1] & ADDRESS_DIV_P_EL)
7200                      ) {
7201                ## 3.
7202                !!!cp ('t357.1');
7203                last; ## goto 5.
7204              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7205                !!!cp ('t358.1');
7206                #
7207            } else {            } else {
7208              !!!cp ('t364');              !!!cp ('t359.1');
7209            }              $non_optional ||= $node;
7210                          #
           ## 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;  
7211            }            }
7212                        ## 4.
7213            !!!cp ('t366');            ## goto 2.
           ## Step 4  
7214            $i--;            $i--;
7215            $node = $self->{open_elements}->[$i];          }
7216            redo LI;  
7217          } # LI          ## 5. (a) has a |p| element in scope
7218                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7219          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            if ($_->[1] == P_EL) {
7220                !!!cp ('t353.1');
7221                !!!back-token; # <x>
7222                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7223                          line => $token->{line}, column => $token->{column}};
7224                next B;
7225              } elsif ($_->[1] & SCOPING_EL) {
7226                !!!cp ('t354.1');
7227                last INSCOPE;
7228              }
7229            } # INSCOPE
7230    
7231            ## 5. (b) insert
7232            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7233            !!!nack ('t359.2');
7234          !!!next-token;          !!!next-token;
7235          redo B;          next B;
7236        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7237            ## NOTE: As normal, but effectively ends parsing
7238    
7239          ## has a p element in scope          ## has a p element in scope
7240          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7241            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7242              !!!cp ('t367');              !!!cp ('t367');
7243              !!!back-token;              !!!back-token; # <plaintext>
7244              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7245              redo B;                        line => $token->{line}, column => $token->{column}};
7246            } elsif ({              next B;
7247                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7248              !!!cp ('t368');              !!!cp ('t368');
7249              last INSCOPE;              last INSCOPE;
7250            }            }
7251          } # INSCOPE          } # INSCOPE
7252                        
7253          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7254                        
7255          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7256                        
7257            !!!nack ('t368.1');
7258          !!!next-token;          !!!next-token;
7259          redo B;          next B;
7260        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7261          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7262            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7263            if ($node->[1] eq 'a') {            if ($node->[1] == A_EL) {
7264              !!!cp ('t371');              !!!cp ('t371');
7265              !!!parse-error (type => 'in a:a');              !!!parse-error (type => 'in a:a', token => $token);
7266                            
7267              !!!back-token;              !!!back-token; # <a>
7268              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7269              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7270                $formatting_end_tag->($token);
7271                            
7272              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7273                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
# Line 5720  sub _tree_construction_main ($) { Line 7292  sub _tree_construction_main ($) {
7292                        
7293          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7294    
7295          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7296          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7297    
7298            !!!nack ('t374.1');
7299          !!!next-token;          !!!next-token;
7300          redo B;          next B;
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         !!!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;  
7301        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7302          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7303    
7304          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7305          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7306            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7307            if ($node->[1] eq 'nobr') {            if ($node->[1] == NOBR_EL) {
7308              !!!cp ('t376');              !!!cp ('t376');
7309              !!!parse-error (type => 'in nobr:nobr');              !!!parse-error (type => 'in nobr:nobr', token => $token);
7310              !!!back-token;              !!!back-token; # <nobr>
7311              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7312              redo B;                        line => $token->{line}, column => $token->{column}};
7313            } elsif ({              next B;
7314                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7315              !!!cp ('t377');              !!!cp ('t377');
7316              last INSCOPE;              last INSCOPE;
7317            }            }
7318          } # INSCOPE          } # INSCOPE
7319                    
7320          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7321          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7322                    
7323            !!!nack ('t377.1');
7324          !!!next-token;          !!!next-token;
7325          redo B;          next B;
7326        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7327          ## has a button element in scope          ## has a button element in scope
7328          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7329            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7330            if ($node->[1] eq 'button') {            if ($node->[1] == BUTTON_EL) {
7331              !!!cp ('t378');              !!!cp ('t378');
7332              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button', token => $token);
7333              !!!back-token;              !!!back-token; # <button>
7334              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7335              redo B;                        line => $token->{line}, column => $token->{column}};
7336            } elsif ({              next B;
7337                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7338              !!!cp ('t379');              !!!cp ('t379');
7339              last INSCOPE;              last INSCOPE;
7340            }            }
# Line 5785  sub _tree_construction_main ($) { Line 7342  sub _tree_construction_main ($) {
7342                        
7343          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7344                        
7345          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7346    
7347          ## TODO: associate with $self->{form_element} if defined          ## TODO: associate with $self->{form_element} if defined
7348    
7349          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7350    
7351            !!!nack ('t379.1');
7352          !!!next-token;          !!!next-token;
7353          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;  
7354        } elsif ({        } elsif ({
7355                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7356                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7357                  image => 1,                  noembed => 1,
7358                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7359                    noscript => 0, ## TODO: 1 if scripting is enabled
7360                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7361          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7362            !!!cp ('t384');            !!!cp ('t381');
7363            !!!parse-error (type => 'image');            $reconstruct_active_formatting_elements->($insert_to_current);
           $token->{tag_name} = 'img';  
7364          } else {          } else {
7365            !!!cp ('t385');            !!!cp ('t399');
7366          }          }
7367            ## NOTE: There is an "as if in body" code clone.
7368          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7369          $reconstruct_active_formatting_elements->($insert_to_current);          next B;
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!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;  
7370        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7371          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7372                    
7373          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7374            !!!cp ('t389');            !!!cp ('t389');
7375            ## Ignore the token            ## Ignore the token
7376              !!!nack ('t389'); ## NOTE: Not acknowledged.
7377            !!!next-token;            !!!next-token;
7378            redo B;            next B;
7379          } else {          } else {
7380              !!!ack ('t391.1');
7381    
7382            my $at = $token->{attributes};            my $at = $token->{attributes};
7383            my $form_attrs;            my $form_attrs;
7384            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5903  sub _tree_construction_main ($) { Line 7388  sub _tree_construction_main ($) {
7388            delete $at->{prompt};            delete $at->{prompt};
7389            my @tokens = (            my @tokens = (
7390                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7391                           attributes => $form_attrs},                           attributes => $form_attrs,
7392                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7393                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7394                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7395                            {type => START_TAG_TOKEN, tag_name => 'p',
7396                             line => $token->{line}, column => $token->{column}},
7397                            {type => START_TAG_TOKEN, tag_name => 'label',
7398                             line => $token->{line}, column => $token->{column}},
7399                         );                         );
7400            if ($prompt_attr) {            if ($prompt_attr) {
7401              !!!cp ('t390');              !!!cp ('t390');
7402              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7403                               #line => $token->{line}, column => $token->{column},
7404                              };
7405            } else {            } else {
7406              !!!cp ('t391');              !!!cp ('t391');
7407              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7408                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7409                               #line => $token->{line}, column => $token->{column},
7410                              }; # SHOULD
7411              ## TODO: make this configurable              ## TODO: make this configurable
7412            }            }
7413            push @tokens,            push @tokens,
7414                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7415                             line => $token->{line}, column => $token->{column}},
7416                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7417                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7418                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7419                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7420                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7421            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7422                             line => $token->{line}, column => $token->{column}},
7423                            {type => END_TAG_TOKEN, tag_name => 'form',
7424                             line => $token->{line}, column => $token->{column}};
7425            !!!back-token (@tokens);            !!!back-token (@tokens);
7426            redo B;            !!!next-token;
7427              next B;
7428          }          }
7429        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7430          my $tag_name = $token->{tag_name};          ## Step 1
7431          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
7432                    
7433            ## Step 2
7434          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7435    
7436            ## Step 3
7437            $self->{ignore_newline} = 1;
7438    
7439            ## Step 4
7440            ## ISSUE: This step is wrong. (r2302 enbugged)
7441    
7442            ## Step 5
7443          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
7444          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
7445            
7446          $insert->($el);          ## Step 6-7
7447                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7448          my $text = '';  
7449            !!!nack ('t392.1');
7450          !!!next-token;          !!!next-token;
7451          if ($token->{type} == CHARACTER_TOKEN) {          next B;
7452            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
7453            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
7454              !!!cp ('t392');          ## has an |option| element in scope
7455              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7456            } else {            my $node = $self->{open_elements}->[$_];
7457              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
7458                !!!cp ('t397.1');
7459                ## NOTE: As if </option>
7460                !!!back-token; # <option> or <optgroup>
7461                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7462                          line => $token->{line}, column => $token->{column}};
7463                next B;
7464              } elsif ($node->[1] & SCOPING_EL) {
7465                !!!cp ('t397.2');
7466                last INSCOPE;
7467            }            }
7468          } else {          } # INSCOPE
7469            !!!cp ('t394');  
7470          }          $reconstruct_active_formatting_elements->($insert_to_current);
7471          while ($token->{type} == CHARACTER_TOKEN) {  
7472            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7473            $text .= $token->{data};  
7474            !!!next-token;          !!!nack ('t397.3');
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
         }  
7475          !!!next-token;          !!!next-token;
7476          redo B;          redo B;
7477        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7478                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7479                  noembed => 1,          ## has a |ruby| element in scope
7480                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7481                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7482                 }->{$token->{tag_name}}) {            if ($node->[1] == RUBY_EL) {
7483          !!!cp ('t399');              !!!cp ('t398.1');
7484          ## NOTE: There is an "as if in body" code clone.              ## generate implied end tags
7485          $parse_rcdata->(CDATA_CONTENT_MODEL);              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7486                  !!!cp ('t398.2');
7487                  pop @{$self->{open_elements}};
7488                }
7489                unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
7490                  !!!cp ('t398.3');
7491                  !!!parse-error (type => 'not closed',
7492                                  text => $self->{open_elements}->[-1]->[0]
7493                                      ->manakai_local_name,
7494                                  token => $token);
7495                  pop @{$self->{open_elements}}
7496                      while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
7497                }
7498                last INSCOPE;
7499              } elsif ($node->[1] & SCOPING_EL) {
7500                !!!cp ('t398.4');
7501                last INSCOPE;
7502              }
7503            } # INSCOPE
7504    
7505            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7506    
7507            !!!nack ('t398.5');
7508            !!!next-token;
7509          redo B;          redo B;
7510        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7511          !!!cp ('t400');                 $token->{tag_name} eq 'svg') {
7512          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
7513    
7514          ## TODO: associate with $self->{form_element} if defined          ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7515    
7516            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7517    
7518            ## "adjust foreign attributes" - done in insert-element-f
7519            
7520            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7521                    
7522          if ($self->{insertion_mode} & TABLE_IMS or          if ($self->{self_closing}) {
7523              $self->{insertion_mode} & BODY_TABLE_IMS or            pop @{$self->{open_elements}};
7524              $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {            !!!ack ('t398.6');
           !!!cp ('t400.1');  
           $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;  
7525          } else {          } else {
7526            !!!cp ('t400.2');            !!!cp ('t398.7');
7527            $self->{insertion_mode} = IN_SELECT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7528              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7529              ## mode, "in body" (not "in foreign content") secondary insertion
7530              ## mode, maybe.
7531          }          }
7532    
7533          !!!next-token;          !!!next-token;
7534          redo B;          next B;
7535        } elsif ({        } elsif ({
7536                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7537                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7538                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7539                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7540                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7541          !!!cp ('t401');          !!!cp ('t401');
7542          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'in body',
7543                            text => $token->{tag_name}, token => $token);
7544          ## Ignore the token          ## Ignore the token
7545            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7546            !!!next-token;
7547            next B;
7548          } elsif ($token->{tag_name} eq 'param' or
7549                   $token->{tag_name} eq 'source') {
7550            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7551            pop @{$self->{open_elements}};
7552    
7553            !!!ack ('t398.5');
7554          !!!next-token;          !!!next-token;
7555          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7556        } else {        } else {
7557          !!!cp ('t402');          if ($token->{tag_name} eq 'image') {
7558              !!!cp ('t384');
7559              !!!parse-error (type => 'image', token => $token);
7560              $token->{tag_name} = 'img';
7561            } else {
7562              !!!cp ('t385');
7563            }
7564    
7565            ## NOTE: There is an "as if <br>" code clone.
7566          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7567                    
7568          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7569    
7570            if ({
7571                 applet => 1, marquee => 1, object => 1,
7572                }->{$token->{tag_name}}) {
7573              !!!cp ('t380');
7574              push @$active_formatting_elements, ['#marker', ''];
7575              !!!nack ('t380.1');
7576            } elsif ({
7577                      b => 1, big => 1, em => 1, font => 1, i => 1,
7578                      s => 1, small => 1, strike => 1,
7579                      strong => 1, tt => 1, u => 1,
7580                     }->{$token->{tag_name}}) {
7581              !!!cp ('t375');
7582              push @$active_formatting_elements, $self->{open_elements}->[-1];
7583              !!!nack ('t375.1');
7584            } elsif ($token->{tag_name} eq 'input') {
7585              !!!cp ('t388');
7586              ## TODO: associate with $self->{form_element} if defined
7587              pop @{$self->{open_elements}};
7588              !!!ack ('t388.2');
7589            } elsif ({
7590                      area => 1, basefont => 1, bgsound => 1, br => 1,
7591                      embed => 1, img => 1, spacer => 1, wbr => 1,
7592                     }->{$token->{tag_name}}) {
7593              !!!cp ('t388.1');
7594              pop @{$self->{open_elements}};
7595              !!!ack ('t388.3');
7596            } elsif ($token->{tag_name} eq 'select') {
7597              ## TODO: associate with $self->{form_element} if defined
7598            
7599              if ($self->{insertion_mode} & TABLE_IMS or
7600                  $self->{insertion_mode} & BODY_TABLE_IMS or
7601                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7602                !!!cp ('t400.1');
7603                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7604              } else {
7605                !!!cp ('t400.2');
7606                $self->{insertion_mode} = IN_SELECT_IM;
7607              }
7608              !!!nack ('t400.3');
7609            } else {
7610              !!!nack ('t402');
7611            }
7612                    
7613          !!!next-token;          !!!next-token;
7614          redo B;          next B;
7615        }        }
7616      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7617        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7618          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7619              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7620            for (@{$self->{open_elements}}) {          INSCOPE: {
7621              unless ({            for (reverse @{$self->{open_elements}}) {
7622                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] == BODY_EL) {
7623                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7624                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7625                      }->{$_->[1]}) {                last INSCOPE;
7626                !!!cp ('t403');              } elsif ($_->[1] & SCOPING_EL) {
7627                !!!parse-error (type => 'not closed:'.$_->[1]);                !!!cp ('t405.1');
7628              } else {                last;
               !!!cp ('t404');  
7629              }              }
7630            }            }
7631    
7632            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7633            !!!next-token;  
7634            redo B;            !!!parse-error (type => 'unmatched end tag',
7635          } else {                            text => $token->{tag_name}, token => $token);
7636            !!!cp ('t405');            ## NOTE: Ignore the token.
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
7637            !!!next-token;            !!!next-token;
7638            redo B;            next B;
7639            } # INSCOPE
7640    
7641            for (@{$self->{open_elements}}) {
7642              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7643                !!!cp ('t403');
7644                !!!parse-error (type => 'not closed',
7645                                text => $_->[0]->manakai_local_name,
7646                                token => $token);
7647                last;
7648              } else {
7649                !!!cp ('t404');
7650              }
7651          }          }
7652    
7653            $self->{insertion_mode} = AFTER_BODY_IM;
7654            !!!next-token;
7655            next B;
7656        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7657          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {          ## TODO: Update this code.  It seems that the code below is not
7658            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7659            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7660                $self->{open_elements}->[1]->[1] == BODY_EL) {
7661              unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
7662              !!!cp ('t406');              !!!cp ('t406');
7663              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!parse-error (type => 'not closed',
7664                                text => $self->{open_elements}->[1]->[0]
7665                                    ->manakai_local_name,
7666                                token => $token);
7667            } else {            } else {
7668              !!!cp ('t407');              !!!cp ('t407');
7669            }            }
7670            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7671            ## reprocess            ## reprocess
7672            redo B;            next B;
7673          } else {          } else {
7674            !!!cp ('t408');            !!!cp ('t408');
7675            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7676                              text => $token->{tag_name}, token => $token);
7677            ## Ignore the token            ## Ignore the token
7678            !!!next-token;            !!!next-token;
7679            redo B;            next B;
7680          }          }
7681        } elsif ({        } elsif ({
7682                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7683                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7684                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7685                    address => 1, article => 1, aside => 1, blockquote => 1,
7686                    center => 1, datagrid => 1, details => 1, dialog => 1,
7687                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7688                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7689                    ol => 1, pre => 1, section => 1, ul => 1,
7690    
7691                    ## NOTE: As normal, but ... optional tags
7692                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7693                  button => 1, marquee => 1, object => 1,  
7694                    applet => 1, button => 1, marquee => 1, object => 1,
7695                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7696            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7697            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7698            ## </dd>" code.
7699    
7700          ## has an element in scope          ## has an element in scope
7701          my $i;          my $i;
7702          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7703            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7704            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7705              !!!cp ('t410');              !!!cp ('t410');
7706              $i = $_;              $i = $_;
7707              last INSCOPE;              last INSCOPE;
7708            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7709              !!!cp ('t411');              !!!cp ('t411');
7710              last INSCOPE;              last INSCOPE;
7711            }            }
# Line 6097  sub _tree_construction_main ($) { Line 7713  sub _tree_construction_main ($) {
7713    
7714          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7715            !!!cp ('t413');            !!!cp ('t413');
7716            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7717                              text => $token->{tag_name}, token => $token);
7718              ## NOTE: Ignore the token.
7719          } else {          } else {
7720            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7721            while ({            while ({
7722                      ## END_TAG_OPTIONAL_EL
7723                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7724                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7725                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7726                      option => 1,
7727                      optgroup => 1,
7728                    p => 1,                    p => 1,
7729                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7730                      rp => 1,
7731                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7732              !!!cp ('t409');              !!!cp ('t409');
7733              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7734            }            }
7735    
7736            ## Step 2.            ## Step 2.
7737            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7738                      ne $token->{tag_name}) {
7739              !!!cp ('t412');              !!!cp ('t412');
7740              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7741                                text => $self->{open_elements}->[-1]->[0]
7742                                    ->manakai_local_name,
7743                                token => $token);
7744            } else {            } else {
7745              !!!cp ('t414');              !!!cp ('t414');
7746            }            }
# Line 6124  sub _tree_construction_main ($) { Line 7751  sub _tree_construction_main ($) {
7751            ## Step 4.            ## Step 4.
7752            $clear_up_to_marker->()            $clear_up_to_marker->()
7753                if {                if {
7754                  button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7755                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7756          }          }
7757          !!!next-token;          !!!next-token;
7758          redo B;          next B;
7759        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7760            ## NOTE: As normal, but interacts with the form element pointer
7761    
7762          undef $self->{form_element};          undef $self->{form_element};
7763    
7764          ## has an element in scope          ## has an element in scope
7765          my $i;          my $i;
7766          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7767            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7768            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] == FORM_EL) {
7769              !!!cp ('t418');              !!!cp ('t418');
7770              $i = $_;              $i = $_;
7771              last INSCOPE;              last INSCOPE;
7772            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7773              !!!cp ('t419');              !!!cp ('t419');
7774              last INSCOPE;              last INSCOPE;
7775            }            }
# Line 6151  sub _tree_construction_main ($) { Line 7777  sub _tree_construction_main ($) {
7777    
7778          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7779            !!!cp ('t421');            !!!cp ('t421');
7780            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7781                              text => $token->{tag_name}, token => $token);
7782              ## NOTE: Ignore the token.
7783          } else {          } else {
7784            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7785            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7786              !!!cp ('t417');              !!!cp ('t417');
7787              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7788            }            }
7789                        
7790            ## Step 2.            ## Step 2.
7791            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7792                      ne $token->{tag_name}) {
7793              !!!cp ('t417.1');              !!!cp ('t417.1');
7794              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7795                                text => $self->{open_elements}->[-1]->[0]
7796                                    ->manakai_local_name,
7797                                token => $token);
7798            } else {            } else {
7799              !!!cp ('t420');              !!!cp ('t420');
7800            }              }  
# Line 6174  sub _tree_construction_main ($) { Line 7804  sub _tree_construction_main ($) {
7804          }          }
7805    
7806          !!!next-token;          !!!next-token;
7807          redo B;          next B;
7808        } elsif ({        } elsif ({
7809                    ## NOTE: As normal, except acts as a closer for any ...
7810                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7811                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7812          ## has an element in scope          ## has an element in scope
7813          my $i;          my $i;
7814          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7815            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7816            if ({            if ($node->[1] == HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7817              !!!cp ('t423');              !!!cp ('t423');
7818              $i = $_;              $i = $_;
7819              last INSCOPE;              last INSCOPE;
7820            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7821              !!!cp ('t424');              !!!cp ('t424');
7822              last INSCOPE;              last INSCOPE;
7823            }            }
# Line 6199  sub _tree_construction_main ($) { Line 7825  sub _tree_construction_main ($) {
7825    
7826          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7827            !!!cp ('t425.1');            !!!cp ('t425.1');
7828            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7829                              text => $token->{tag_name}, token => $token);
7830              ## NOTE: Ignore the token.
7831          } else {          } else {
7832            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7833            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7834              !!!cp ('t422');              !!!cp ('t422');
7835              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7836            }            }
7837                        
7838            ## Step 2.            ## Step 2.
7839            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7840                      ne $token->{tag_name}) {
7841              !!!cp ('t425');              !!!cp ('t425');
7842              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
7843                                text => $token->{tag_name}, token => $token);
7844            } else {            } else {
7845              !!!cp ('t426');              !!!cp ('t426');
7846            }            }
# Line 6222  sub _tree_construction_main ($) { Line 7850  sub _tree_construction_main ($) {
7850          }          }
7851                    
7852          !!!next-token;          !!!next-token;
7853          redo B;          next B;
7854        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7855            ## NOTE: As normal, except </p> implies <p> and ...
7856    
7857          ## has an element in scope          ## has an element in scope
7858            my $non_optional;
7859          my $i;          my $i;
7860          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7861            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7862            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] == P_EL) {
7863              !!!cp ('t410.1');              !!!cp ('t410.1');
7864              $i = $_;              $i = $_;
7865              last INSCOPE;              last INSCOPE;
7866            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7867              !!!cp ('t411.1');              !!!cp ('t411.1');
7868              last INSCOPE;              last INSCOPE;
7869              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7870                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7871                !!!cp ('t411.2');
7872                #
7873              } else {
7874                !!!cp ('t411.3');
7875                $non_optional ||= $node;
7876                #
7877            }            }
7878          } # INSCOPE          } # INSCOPE
7879    
7880          if (defined $i) {          if (defined $i) {
7881            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            ## 1. Generate implied end tags
7882              #
7883    
7884              ## 2. If current node != "p", parse error
7885              if ($non_optional) {
7886              !!!cp ('t412.1');              !!!cp ('t412.1');
7887              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7888                                text => $non_optional->[0]->manakai_local_name,
7889                                token => $token);
7890            } else {            } else {
7891              !!!cp ('t414.1');              !!!cp ('t414.1');
7892            }            }
7893    
7894              ## 3. Pop
7895            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7896          } else {          } else {
7897            !!!cp ('t413.1');            !!!cp ('t413.1');
7898            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7899                              text => $token->{tag_name}, token => $token);
7900    
7901            !!!cp ('t415.1');            !!!cp ('t415.1');
7902            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7903            my $el;            my $el;
7904            !!!create-element ($el, 'p');            !!!create-element ($el, $HTML_NS, 'p',, $token);
7905            $insert->($el);            $insert->($el);
7906            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7907          }          }
7908    
7909          !!!next-token;          !!!next-token;
7910          redo B;          next B;
7911        } elsif ({        } elsif ({
7912                  a => 1,                  a => 1,
7913                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7914                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7915                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7916                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7917          !!!cp ('t427');          !!!cp ('t427');
7918          $formatting_end_tag->($token->{tag_name});          $formatting_end_tag->($token);
7919          redo B;          next B;
7920        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7921          !!!cp ('t428');          !!!cp ('t428');
7922          !!!parse-error (type => 'unmatched end tag:br');          !!!parse-error (type => 'unmatched end tag',
7923                            text => 'br', token => $token);
7924    
7925          ## As if <br>          ## As if <br>
7926          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7927                    
7928          my $el;          my $el;
7929          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7930          $insert->($el);          $insert->($el);
7931                    
7932          ## Ignore the token.          ## Ignore the token.
7933          !!!next-token;          !!!next-token;
7934          redo B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!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  
           
7935        } else {        } else {
7936            if ($token->{tag_name} eq 'sarcasm') {
7937              sleep 0.001; # take a deep breath
7938            }
7939    
7940          ## Step 1          ## Step 1
7941          my $node_i = -1;          my $node_i = -1;
7942          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7943    
7944          ## Step 2          ## Step 2
7945          S2: {          S2: {
7946            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7947              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7948              if ($node_tag_name eq $token->{tag_name}) {
7949              ## Step 1              ## Step 1
7950              ## generate implied end tags              ## generate implied end tags
7951              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7952                !!!cp ('t430');                !!!cp ('t430');
7953                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7954                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7955                  ## which seems wrong.
7956                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7957                  $node_i++;
7958              }              }
7959                    
7960              ## Step 2              ## Step 2
7961              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7962                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7963                $current_tag_name =~ tr/A-Z/a-z/;
7964                if ($current_tag_name ne $token->{tag_name}) {
7965                !!!cp ('t431');                !!!cp ('t431');
7966                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7967                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7968                                  text => $self->{open_elements}->[-1]->[0]
7969                                      ->manakai_local_name,
7970                                  token => $token);
7971              } else {              } else {
7972                !!!cp ('t432');                !!!cp ('t432');
7973              }              }
7974                            
7975              ## Step 3              ## Step 3
7976              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7977    
7978              !!!next-token;              !!!next-token;
7979              last S2;              last S2;
7980            } else {            } else {
7981              ## Step 3              ## Step 3
7982              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7983                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7984                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7985                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7986                !!!cp ('t433');                !!!cp ('t433');
7987                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
7988                                  text => $token->{tag_name}, token => $token);
7989                ## Ignore the token                ## Ignore the token
7990                !!!next-token;                !!!next-token;
7991                last S2;                last S2;
             }  
7992    
7993                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7994                  ## 9.27, "a" is a child of <dd> (conforming).  In
7995                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7996                  ## "a" is a child of both <body> and <dd>.
7997                }
7998                
7999              !!!cp ('t434');              !!!cp ('t434');
8000            }            }
8001                        
# Line 6362  sub _tree_construction_main ($) { Line 8006  sub _tree_construction_main ($) {
8006            ## Step 5;            ## Step 5;
8007            redo S2;            redo S2;
8008          } # S2          } # S2
8009          redo B;          next B;
8010        }        }
8011      }      }
8012      redo B;      next B;
8013      } continue { # B
8014        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8015          ## NOTE: The code below is executed in cases where it does not have
8016          ## to be, but it it is harmless even in those cases.
8017          ## has an element in scope
8018          INSCOPE: {
8019            for (reverse 0..$#{$self->{open_elements}}) {
8020              my $node = $self->{open_elements}->[$_];
8021              if ($node->[1] & FOREIGN_EL) {
8022                last INSCOPE;
8023              } elsif ($node->[1] & SCOPING_EL) {
8024                last;
8025              }
8026            }
8027            
8028            ## NOTE: No foreign element in scope.
8029            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8030          } # INSCOPE
8031        }
8032    } # B    } # B
8033    
8034    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6373  sub _tree_construction_main ($) { Line 8036  sub _tree_construction_main ($) {
8036    ## TODO: script stuffs    ## TODO: script stuffs
8037  } # _tree_construct_main  } # _tree_construct_main
8038    
8039  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8040    my $class = shift;    my $class = shift;
8041    my $node = shift;    my $node = shift;
8042    my $s = \$_[0];    #my $s = \$_[0];
8043    my $onerror = $_[1];    my $onerror = $_[1];
8044      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8045    
8046    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
8047    
# Line 6396  sub set_inner_html ($$$) { Line 8060  sub set_inner_html ($$$) {
8060      }      }
8061    
8062      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8063      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8064    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8065      ## TODO: If non-html element      ## TODO: If non-html element
8066    
8067      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8068    
8069    ## TODO: Support for $get_wrapper
8070    
8071      ## Step 1 # MUST      ## Step 1 # MUST
8072      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8073      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6411  sub set_inner_html ($$$) { Line 8077  sub set_inner_html ($$$) {
8077    
8078      ## Step 8 # MUST      ## Step 8 # MUST
8079      my $i = 0;      my $i = 0;
8080      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8081      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8082      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8083        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8084        $input = $get_wrapper->($input);
8085        $p->{set_nc} = sub {
8086        my $self = shift;        my $self = shift;
8087    
8088        pop @{$self->{prev_char}};        my $char = '';
8089        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8090            $char = $self->{next_nc};
8091            delete $self->{next_nc};
8092            $self->{nc} = ord $char;
8093          } else {
8094            $self->{char_buffer} = '';
8095            $self->{char_buffer_pos} = 0;
8096            
8097            my $count = $input->manakai_read_until
8098                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8099                 $self->{char_buffer_pos});
8100            if ($count) {
8101              $self->{line_prev} = $self->{line};
8102              $self->{column_prev} = $self->{column};
8103              $self->{column}++;
8104              $self->{nc}
8105                  = ord substr ($self->{char_buffer},
8106                                $self->{char_buffer_pos}++, 1);
8107              return;
8108            }
8109            
8110            if ($input->read ($char, 1)) {
8111              $self->{nc} = ord $char;
8112            } else {
8113              $self->{nc} = -1;
8114              return;
8115            }
8116          }
8117    
8118        $self->{next_char} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8119        $self->{next_char} = ord substr $$s, $i++, 1;        $p->{column}++;
8120        $column++;  
8121          if ($self->{nc} == 0x000A) { # LF
8122        if ($self->{next_char} == 0x000A) { # LF          $p->{line}++;
8123          $line++;          $p->{column} = 0;
         $column = 0;  
8124          !!!cp ('i1');          !!!cp ('i1');
8125        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8126          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8127          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8128          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8129          $column = 0;            $self->{next_nc} = $next;
8130            }
8131            $self->{nc} = 0x000A; # LF # MUST
8132            $p->{line}++;
8133            $p->{column} = 0;
8134          !!!cp ('i2');          !!!cp ('i2');
8135        } 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  
8136          !!!cp ('i4');          !!!cp ('i4');
8137          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8138          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8139        }        }
8140      };      };
8141      $p->{prev_char} = [-1, -1, -1];  
8142      $p->{next_char} = -1;      $p->{read_until} = sub {
8143              #my ($scalar, $specials_range, $offset) = @_;
8144          return 0 if defined $p->{next_nc};
8145    
8146          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8147          my $offset = $_[2] || 0;
8148          
8149          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8150            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8151            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8152              substr ($_[0], $offset)
8153                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8154              my $count = $+[0] - $-[0];
8155              if ($count) {
8156                $p->{column} += $count;
8157                $p->{char_buffer_pos} += $count;
8158                $p->{line_prev} = $p->{line};
8159                $p->{column_prev} = $p->{column} - 1;
8160                $p->{nc} = -1;
8161              }
8162              return $count;
8163            } else {
8164              return 0;
8165            }
8166          } else {
8167            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8168            if ($count) {
8169              $p->{column} += $count;
8170              $p->{column_prev} += $count;
8171              $p->{nc} = -1;
8172            }
8173            return $count;
8174          }
8175        }; # $p->{read_until}
8176    
8177      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8178        my (%opt) = @_;        my (%opt) = @_;
8179        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8180          my $column = $opt{column};
8181          if (defined $opt{token} and defined $opt{token}->{line}) {
8182            $line = $opt{token}->{line};
8183            $column = $opt{token}->{column};
8184          }
8185          warn "Parse error ($opt{type}) at line $line column $column\n";
8186      };      };
8187      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8188        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8189      };      };
8190            
8191        my $char_onerror = sub {
8192          my (undef, $type, %opt) = @_;
8193          $ponerror->(layer => 'encode',
8194                      line => $p->{line}, column => $p->{column} + 1,
8195                      %opt, type => $type);
8196        }; # $char_onerror
8197        $input->onerror ($char_onerror);
8198    
8199      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8200      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8201    
# Line 6474  sub set_inner_html ($$$) { Line 8217  sub set_inner_html ($$$) {
8217          unless defined $p->{content_model};          unless defined $p->{content_model};
8218          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8219    
8220      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8221          ## TODO: Foreign element OK?
8222    
8223      ## Step 3      ## Step 3
8224      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6484  sub set_inner_html ($$$) { Line 8228  sub set_inner_html ($$$) {
8228      $doc->append_child ($root);      $doc->append_child ($root);
8229    
8230      ## Step 5 # MUST      ## Step 5 # MUST
8231      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8232    
8233      undef $p->{head_element};      undef $p->{head_element};
8234        undef $p->{head_element_inserted};
8235    
8236      ## Step 6 # MUST      ## Step 6 # MUST
8237      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
# Line 6530  sub set_inner_html ($$$) { Line 8275  sub set_inner_html ($$$) {
8275      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8276    
8277      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8278    
8279        delete $p->{parse_error}; # delete loop
8280    } else {    } else {
8281      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
8282    }    }

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24