/[suikacvs]/markup/html/whatpm/Whatpm/HTML.pm.src
Suika

Contents of /markup/html/whatpm/Whatpm/HTML.pm.src

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.205 - (hide annotations) (download) (as text)
Mon Oct 13 06:18:31 2008 UTC (17 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.204: +124 -129 lines
File MIME type: application/x-wais-source
++ whatpm/t/ChangeLog	13 Oct 2008 06:18:26 -0000
2008-10-13  Wakaba  <wakaba@suika.fam.cx>

	* tokenizer-test-2.dat: A test result was wrong.

++ whatpm/Whatpm/ChangeLog	13 Oct 2008 06:17:59 -0000
2008-10-13  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm.src: Steps for CDATA/RCDATA elements in tree
	construction stage synced with the spec (HTML5 revisions 2139 and
	2302).

1 wakaba 1.2 package Whatpm::HTML;
2 wakaba 1.1 use strict;
3 wakaba 1.205 our $VERSION=do{my @r=(q$Revision: 1.204 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 wakaba 1.63 use Error qw(:try);
5 wakaba 1.1
6 wakaba 1.182 ## 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 wakaba 1.18 ## ISSUE:
18     ## var doc = implementation.createDocument (null, null, null);
19     ## doc.write ('');
20     ## alert (doc.compatMode);
21 wakaba 1.1
22 wakaba 1.139 require IO::Handle;
23    
24 wakaba 1.126 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25     my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26     my $SVG_NS = q<http://www.w3.org/2000/svg>;
27     my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28     my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29     my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    
31 wakaba 1.123 sub A_EL () { 0b1 }
32     sub ADDRESS_EL () { 0b10 }
33     sub BODY_EL () { 0b100 }
34     sub BUTTON_EL () { 0b1000 }
35     sub CAPTION_EL () { 0b10000 }
36     sub DD_EL () { 0b100000 }
37     sub DIV_EL () { 0b1000000 }
38     sub DT_EL () { 0b10000000 }
39     sub FORM_EL () { 0b100000000 }
40     sub FORMATTING_EL () { 0b1000000000 }
41     sub FRAMESET_EL () { 0b10000000000 }
42     sub HEADING_EL () { 0b100000000000 }
43     sub HTML_EL () { 0b1000000000000 }
44     sub LI_EL () { 0b10000000000000 }
45     sub NOBR_EL () { 0b100000000000000 }
46     sub OPTION_EL () { 0b1000000000000000 }
47     sub OPTGROUP_EL () { 0b10000000000000000 }
48     sub P_EL () { 0b100000000000000000 }
49     sub SELECT_EL () { 0b1000000000000000000 }
50     sub TABLE_EL () { 0b10000000000000000000 }
51     sub TABLE_CELL_EL () { 0b100000000000000000000 }
52     sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53     sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54     sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55     sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56 wakaba 1.126 sub FOREIGN_EL () { 0b10000000000000000000000000 }
57     sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58     sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59 wakaba 1.151 sub RUBY_EL () { 0b10000000000000000000000000000 }
60     sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61 wakaba 1.123
62     sub TABLE_ROWS_EL () {
63     TABLE_EL |
64     TABLE_ROW_EL |
65     TABLE_ROW_GROUP_EL
66     }
67    
68 wakaba 1.151 ## NOTE: Used in "generate implied end tags" algorithm.
69 wakaba 1.194 ## NOTE: There is a code where a modified version of
70     ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71     ## implementation (search for the algorithm name).
72 wakaba 1.123 sub END_TAG_OPTIONAL_EL () {
73     DD_EL |
74     DT_EL |
75     LI_EL |
76 wakaba 1.194 OPTION_EL |
77     OPTGROUP_EL |
78 wakaba 1.151 P_EL |
79     RUBY_COMPONENT_EL
80 wakaba 1.123 }
81    
82 wakaba 1.151 ## NOTE: Used in </body> and EOF algorithms.
83 wakaba 1.123 sub ALL_END_TAG_OPTIONAL_EL () {
84 wakaba 1.151 DD_EL |
85     DT_EL |
86     LI_EL |
87     P_EL |
88    
89 wakaba 1.201 ## ISSUE: option, optgroup, rt, rp?
90    
91 wakaba 1.123 BODY_EL |
92     HTML_EL |
93     TABLE_CELL_EL |
94     TABLE_ROW_EL |
95     TABLE_ROW_GROUP_EL
96     }
97    
98     sub SCOPING_EL () {
99     BUTTON_EL |
100     CAPTION_EL |
101     HTML_EL |
102     TABLE_EL |
103     TABLE_CELL_EL |
104     MISC_SCOPING_EL
105     }
106    
107     sub TABLE_SCOPING_EL () {
108     HTML_EL |
109     TABLE_EL
110     }
111    
112     sub TABLE_ROWS_SCOPING_EL () {
113     HTML_EL |
114     TABLE_ROW_GROUP_EL
115     }
116    
117     sub TABLE_ROW_SCOPING_EL () {
118     HTML_EL |
119     TABLE_ROW_EL
120     }
121    
122     sub SPECIAL_EL () {
123     ADDRESS_EL |
124     BODY_EL |
125     DIV_EL |
126 wakaba 1.151
127     DD_EL |
128     DT_EL |
129     LI_EL |
130     P_EL |
131    
132 wakaba 1.123 FORM_EL |
133     FRAMESET_EL |
134     HEADING_EL |
135     SELECT_EL |
136     TABLE_ROW_EL |
137     TABLE_ROW_GROUP_EL |
138     MISC_SPECIAL_EL
139     }
140    
141     my $el_category = {
142     a => A_EL | FORMATTING_EL,
143     address => ADDRESS_EL,
144     applet => MISC_SCOPING_EL,
145     area => MISC_SPECIAL_EL,
146 wakaba 1.193 article => MISC_SPECIAL_EL,
147     aside => MISC_SPECIAL_EL,
148 wakaba 1.123 b => FORMATTING_EL,
149     base => MISC_SPECIAL_EL,
150     basefont => MISC_SPECIAL_EL,
151     bgsound => MISC_SPECIAL_EL,
152     big => FORMATTING_EL,
153     blockquote => MISC_SPECIAL_EL,
154     body => BODY_EL,
155     br => MISC_SPECIAL_EL,
156     button => BUTTON_EL,
157     caption => CAPTION_EL,
158     center => MISC_SPECIAL_EL,
159     col => MISC_SPECIAL_EL,
160     colgroup => MISC_SPECIAL_EL,
161 wakaba 1.193 command => MISC_SPECIAL_EL,
162     datagrid => MISC_SPECIAL_EL,
163 wakaba 1.123 dd => DD_EL,
164 wakaba 1.193 details => MISC_SPECIAL_EL,
165     dialog => MISC_SPECIAL_EL,
166 wakaba 1.123 dir => MISC_SPECIAL_EL,
167     div => DIV_EL,
168     dl => MISC_SPECIAL_EL,
169     dt => DT_EL,
170     em => FORMATTING_EL,
171     embed => MISC_SPECIAL_EL,
172 wakaba 1.193 eventsource => MISC_SPECIAL_EL,
173 wakaba 1.123 fieldset => MISC_SPECIAL_EL,
174 wakaba 1.193 figure => MISC_SPECIAL_EL,
175 wakaba 1.123 font => FORMATTING_EL,
176 wakaba 1.193 footer => MISC_SPECIAL_EL,
177 wakaba 1.123 form => FORM_EL,
178     frame => MISC_SPECIAL_EL,
179     frameset => FRAMESET_EL,
180     h1 => HEADING_EL,
181     h2 => HEADING_EL,
182     h3 => HEADING_EL,
183     h4 => HEADING_EL,
184     h5 => HEADING_EL,
185     h6 => HEADING_EL,
186     head => MISC_SPECIAL_EL,
187 wakaba 1.193 header => MISC_SPECIAL_EL,
188 wakaba 1.123 hr => MISC_SPECIAL_EL,
189     html => HTML_EL,
190     i => FORMATTING_EL,
191     iframe => MISC_SPECIAL_EL,
192     img => MISC_SPECIAL_EL,
193 wakaba 1.193 #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194 wakaba 1.123 input => MISC_SPECIAL_EL,
195     isindex => MISC_SPECIAL_EL,
196     li => LI_EL,
197     link => MISC_SPECIAL_EL,
198     listing => MISC_SPECIAL_EL,
199     marquee => MISC_SCOPING_EL,
200     menu => MISC_SPECIAL_EL,
201     meta => MISC_SPECIAL_EL,
202 wakaba 1.193 nav => MISC_SPECIAL_EL,
203 wakaba 1.123 nobr => NOBR_EL | FORMATTING_EL,
204     noembed => MISC_SPECIAL_EL,
205     noframes => MISC_SPECIAL_EL,
206     noscript => MISC_SPECIAL_EL,
207     object => MISC_SCOPING_EL,
208     ol => MISC_SPECIAL_EL,
209     optgroup => OPTGROUP_EL,
210     option => OPTION_EL,
211     p => P_EL,
212     param => MISC_SPECIAL_EL,
213     plaintext => MISC_SPECIAL_EL,
214     pre => MISC_SPECIAL_EL,
215 wakaba 1.151 rp => RUBY_COMPONENT_EL,
216     rt => RUBY_COMPONENT_EL,
217     ruby => RUBY_EL,
218 wakaba 1.123 s => FORMATTING_EL,
219     script => MISC_SPECIAL_EL,
220     select => SELECT_EL,
221 wakaba 1.193 section => MISC_SPECIAL_EL,
222 wakaba 1.123 small => FORMATTING_EL,
223     spacer => MISC_SPECIAL_EL,
224     strike => FORMATTING_EL,
225     strong => FORMATTING_EL,
226     style => MISC_SPECIAL_EL,
227     table => TABLE_EL,
228     tbody => TABLE_ROW_GROUP_EL,
229     td => TABLE_CELL_EL,
230     textarea => MISC_SPECIAL_EL,
231     tfoot => TABLE_ROW_GROUP_EL,
232     th => TABLE_CELL_EL,
233     thead => TABLE_ROW_GROUP_EL,
234     title => MISC_SPECIAL_EL,
235     tr => TABLE_ROW_EL,
236     tt => FORMATTING_EL,
237     u => FORMATTING_EL,
238     ul => MISC_SPECIAL_EL,
239     wbr => MISC_SPECIAL_EL,
240     };
241    
242 wakaba 1.126 my $el_category_f = {
243     $MML_NS => {
244     'annotation-xml' => MML_AXML_EL,
245     mi => FOREIGN_FLOW_CONTENT_EL,
246     mo => FOREIGN_FLOW_CONTENT_EL,
247     mn => FOREIGN_FLOW_CONTENT_EL,
248     ms => FOREIGN_FLOW_CONTENT_EL,
249     mtext => FOREIGN_FLOW_CONTENT_EL,
250     },
251     $SVG_NS => {
252 wakaba 1.200 foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253 wakaba 1.126 desc => FOREIGN_FLOW_CONTENT_EL,
254     title => FOREIGN_FLOW_CONTENT_EL,
255     },
256     ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257     };
258    
259 wakaba 1.131 my $svg_attr_name = {
260 wakaba 1.146 attributename => 'attributeName',
261 wakaba 1.131 attributetype => 'attributeType',
262     basefrequency => 'baseFrequency',
263     baseprofile => 'baseProfile',
264     calcmode => 'calcMode',
265     clippathunits => 'clipPathUnits',
266     contentscripttype => 'contentScriptType',
267     contentstyletype => 'contentStyleType',
268     diffuseconstant => 'diffuseConstant',
269     edgemode => 'edgeMode',
270     externalresourcesrequired => 'externalResourcesRequired',
271     filterres => 'filterRes',
272     filterunits => 'filterUnits',
273     glyphref => 'glyphRef',
274     gradienttransform => 'gradientTransform',
275     gradientunits => 'gradientUnits',
276     kernelmatrix => 'kernelMatrix',
277     kernelunitlength => 'kernelUnitLength',
278     keypoints => 'keyPoints',
279     keysplines => 'keySplines',
280     keytimes => 'keyTimes',
281     lengthadjust => 'lengthAdjust',
282     limitingconeangle => 'limitingConeAngle',
283     markerheight => 'markerHeight',
284     markerunits => 'markerUnits',
285     markerwidth => 'markerWidth',
286     maskcontentunits => 'maskContentUnits',
287     maskunits => 'maskUnits',
288     numoctaves => 'numOctaves',
289     pathlength => 'pathLength',
290     patterncontentunits => 'patternContentUnits',
291     patterntransform => 'patternTransform',
292     patternunits => 'patternUnits',
293     pointsatx => 'pointsAtX',
294     pointsaty => 'pointsAtY',
295     pointsatz => 'pointsAtZ',
296     preservealpha => 'preserveAlpha',
297     preserveaspectratio => 'preserveAspectRatio',
298     primitiveunits => 'primitiveUnits',
299     refx => 'refX',
300     refy => 'refY',
301     repeatcount => 'repeatCount',
302     repeatdur => 'repeatDur',
303     requiredextensions => 'requiredExtensions',
304 wakaba 1.146 requiredfeatures => 'requiredFeatures',
305 wakaba 1.131 specularconstant => 'specularConstant',
306     specularexponent => 'specularExponent',
307     spreadmethod => 'spreadMethod',
308     startoffset => 'startOffset',
309     stddeviation => 'stdDeviation',
310     stitchtiles => 'stitchTiles',
311     surfacescale => 'surfaceScale',
312     systemlanguage => 'systemLanguage',
313     tablevalues => 'tableValues',
314     targetx => 'targetX',
315     targety => 'targetY',
316     textlength => 'textLength',
317     viewbox => 'viewBox',
318     viewtarget => 'viewTarget',
319     xchannelselector => 'xChannelSelector',
320     ychannelselector => 'yChannelSelector',
321     zoomandpan => 'zoomAndPan',
322     };
323    
324     my $foreign_attr_xname = {
325     'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326     'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327     'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328     'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329     'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330     'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331     'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332     'xml:base' => [$XML_NS, ['xml', 'base']],
333     'xml:lang' => [$XML_NS, ['xml', 'lang']],
334     'xml:space' => [$XML_NS, ['xml', 'space']],
335     'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336     'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337     };
338    
339     ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341 wakaba 1.191 my $charref_map = {
342     0x0D => 0x000A,
343 wakaba 1.10 0x80 => 0x20AC,
344     0x81 => 0xFFFD,
345     0x82 => 0x201A,
346     0x83 => 0x0192,
347     0x84 => 0x201E,
348     0x85 => 0x2026,
349     0x86 => 0x2020,
350     0x87 => 0x2021,
351     0x88 => 0x02C6,
352     0x89 => 0x2030,
353     0x8A => 0x0160,
354     0x8B => 0x2039,
355     0x8C => 0x0152,
356     0x8D => 0xFFFD,
357     0x8E => 0x017D,
358     0x8F => 0xFFFD,
359     0x90 => 0xFFFD,
360     0x91 => 0x2018,
361     0x92 => 0x2019,
362     0x93 => 0x201C,
363     0x94 => 0x201D,
364     0x95 => 0x2022,
365     0x96 => 0x2013,
366     0x97 => 0x2014,
367     0x98 => 0x02DC,
368     0x99 => 0x2122,
369     0x9A => 0x0161,
370     0x9B => 0x203A,
371     0x9C => 0x0153,
372     0x9D => 0xFFFD,
373     0x9E => 0x017E,
374     0x9F => 0x0178,
375 wakaba 1.191 }; # $charref_map
376     $charref_map->{$_} = 0xFFFD
377     for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378     0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379     0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380     0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381     0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382     0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383     0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384 wakaba 1.1
385 wakaba 1.192 ## TODO: Invoke the reset algorithm when a resettable element is
386     ## created (cf. HTML5 revision 2259).
387    
388 wakaba 1.63 sub parse_byte_string ($$$$;$) {
389 wakaba 1.138 my $self = shift;
390     my $charset_name = shift;
391     open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392     return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393     } # parse_byte_string
394    
395 wakaba 1.162 sub parse_byte_stream ($$$$;$$) {
396     # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397 wakaba 1.63 my $self = ref $_[0] ? shift : shift->new;
398 wakaba 1.133 my $charset_name = shift;
399 wakaba 1.138 my $byte_stream = $_[0];
400 wakaba 1.133
401 wakaba 1.134 my $onerror = $_[2] || sub {
402     my (%opt) = @_;
403     warn "Parse error ($opt{type})\n";
404     };
405     $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407 wakaba 1.162 my $get_wrapper = $_[3] || sub ($) {
408     return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409     };
410    
411 wakaba 1.133 ## HTML5 encoding sniffing algorithm
412     require Message::Charset::Info;
413     my $charset;
414 wakaba 1.136 my $buffer;
415     my ($char_stream, $e_status);
416 wakaba 1.133
417     SNIFFING: {
418 wakaba 1.160 ## NOTE: By setting |allow_fallback| option true when the
419     ## |get_decode_handle| method is invoked, we ignore what the HTML5
420     ## spec requires, i.e. unsupported encoding should be ignored.
421     ## TODO: We should not do this unless the parser is invoked
422     ## in the conformance checking mode, in which this behavior
423     ## would be useful.
424 wakaba 1.133
425     ## Step 1
426     if (defined $charset_name) {
427 wakaba 1.161 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428     ## TODO: Is this ok? Transfer protocol's parameter should be
429     ## interpreted in its semantics?
430 wakaba 1.133
431 wakaba 1.136 ($char_stream, $e_status) = $charset->get_decode_handle
432     ($byte_stream, allow_error_reporting => 1,
433 wakaba 1.133 allow_fallback => 1);
434 wakaba 1.136 if ($char_stream) {
435 wakaba 1.133 $self->{confident} = 1;
436     last SNIFFING;
437 wakaba 1.136 } else {
438 wakaba 1.190 !!!parse-error (type => 'charset:not supported',
439     layer => 'encode',
440     line => 1, column => 1,
441     value => $charset_name,
442     level => $self->{level}->{uncertain});
443 wakaba 1.133 }
444     }
445    
446     ## Step 2
447 wakaba 1.136 my $byte_buffer = '';
448     for (1..1024) {
449     my $char = $byte_stream->getc;
450     last unless defined $char;
451     $byte_buffer .= $char;
452     } ## TODO: timeout
453 wakaba 1.133
454     ## Step 3
455 wakaba 1.136 if ($byte_buffer =~ /^\xFE\xFF/) {
456 wakaba 1.161 $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457 wakaba 1.136 ($char_stream, $e_status) = $charset->get_decode_handle
458     ($byte_stream, allow_error_reporting => 1,
459     allow_fallback => 1, byte_buffer => \$byte_buffer);
460 wakaba 1.133 $self->{confident} = 1;
461     last SNIFFING;
462 wakaba 1.136 } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463 wakaba 1.161 $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464 wakaba 1.136 ($char_stream, $e_status) = $charset->get_decode_handle
465     ($byte_stream, allow_error_reporting => 1,
466     allow_fallback => 1, byte_buffer => \$byte_buffer);
467 wakaba 1.133 $self->{confident} = 1;
468     last SNIFFING;
469 wakaba 1.136 } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470 wakaba 1.161 $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471 wakaba 1.136 ($char_stream, $e_status) = $charset->get_decode_handle
472     ($byte_stream, allow_error_reporting => 1,
473     allow_fallback => 1, byte_buffer => \$byte_buffer);
474 wakaba 1.133 $self->{confident} = 1;
475     last SNIFFING;
476     }
477    
478     ## Step 4
479     ## TODO: <meta charset>
480    
481     ## Step 5
482     ## TODO: from history
483    
484     ## Step 6
485 wakaba 1.65 require Whatpm::Charset::UniversalCharDet;
486 wakaba 1.133 $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487 wakaba 1.136 ($byte_buffer);
488 wakaba 1.133 if (defined $charset_name) {
489 wakaba 1.161 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490 wakaba 1.133
491 wakaba 1.136 require Whatpm::Charset::DecodeHandle;
492     $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493     ($byte_stream);
494     ($char_stream, $e_status) = $charset->get_decode_handle
495     ($buffer, allow_error_reporting => 1,
496     allow_fallback => 1, byte_buffer => \$byte_buffer);
497     if ($char_stream) {
498     $buffer->{buffer} = $byte_buffer;
499 wakaba 1.153 !!!parse-error (type => 'sniffing:chardet',
500     text => $charset_name,
501     level => $self->{level}->{info},
502     layer => 'encode',
503 wakaba 1.134 line => 1, column => 1);
504 wakaba 1.133 $self->{confident} = 0;
505     last SNIFFING;
506     }
507     }
508    
509     ## Step 7: default
510     ## TODO: Make this configurable.
511 wakaba 1.161 $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512 wakaba 1.133 ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513     ## detectable in the step 6.
514 wakaba 1.136 require Whatpm::Charset::DecodeHandle;
515     $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516     ($byte_stream);
517     ($char_stream, $e_status)
518     = $charset->get_decode_handle ($buffer,
519     allow_error_reporting => 1,
520     allow_fallback => 1,
521     byte_buffer => \$byte_buffer);
522     $buffer->{buffer} = $byte_buffer;
523 wakaba 1.153 !!!parse-error (type => 'sniffing:default',
524     text => 'windows-1252',
525     level => $self->{level}->{info},
526     line => 1, column => 1,
527     layer => 'encode');
528 wakaba 1.63 $self->{confident} = 0;
529 wakaba 1.133 } # SNIFFING
530    
531     if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532 wakaba 1.160 $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533 wakaba 1.153 !!!parse-error (type => 'chardecode:fallback',
534 wakaba 1.160 #text => $self->{input_encoding},
535 wakaba 1.153 level => $self->{level}->{uncertain},
536     line => 1, column => 1,
537     layer => 'encode');
538 wakaba 1.133 } elsif (not ($e_status &
539 wakaba 1.178 Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540 wakaba 1.160 $self->{input_encoding} = $charset->get_iana_name;
541 wakaba 1.153 !!!parse-error (type => 'chardecode:no error',
542     text => $self->{input_encoding},
543     level => $self->{level}->{uncertain},
544     line => 1, column => 1,
545     layer => 'encode');
546 wakaba 1.160 } else {
547     $self->{input_encoding} = $charset->get_iana_name;
548 wakaba 1.63 }
549    
550     $self->{change_encoding} = sub {
551     my $self = shift;
552 wakaba 1.134 $charset_name = shift;
553 wakaba 1.114 my $token = shift;
554 wakaba 1.63
555 wakaba 1.161 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556 wakaba 1.136 ($char_stream, $e_status) = $charset->get_decode_handle
557     ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558     byte_buffer => \ $buffer->{buffer});
559 wakaba 1.134
560 wakaba 1.136 if ($char_stream) { # if supported
561 wakaba 1.134 ## "Change the encoding" algorithm:
562 wakaba 1.63
563 wakaba 1.134 ## Step 1
564 wakaba 1.149 if ($charset->{category} &
565     Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566 wakaba 1.161 $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567 wakaba 1.136 ($char_stream, $e_status) = $charset->get_decode_handle
568     ($byte_stream,
569     byte_buffer => \ $buffer->{buffer});
570 wakaba 1.134 }
571     $charset_name = $charset->get_iana_name;
572    
573     ## Step 2
574     if (defined $self->{input_encoding} and
575     $self->{input_encoding} eq $charset_name) {
576 wakaba 1.153 !!!parse-error (type => 'charset label:matching',
577     text => $charset_name,
578     level => $self->{level}->{info});
579 wakaba 1.134 $self->{confident} = 1;
580     return;
581     }
582 wakaba 1.63
583 wakaba 1.153 !!!parse-error (type => 'charset label detected',
584     text => $self->{input_encoding},
585     value => $charset_name,
586     level => $self->{level}->{warn},
587     token => $token);
588 wakaba 1.134
589     ## Step 3
590     # if (can) {
591     ## change the encoding on the fly.
592     #$self->{confident} = 1;
593     #return;
594     # }
595    
596     ## Step 4
597     throw Whatpm::HTML::RestartParser ();
598 wakaba 1.63 }
599     }; # $self->{change_encoding}
600    
601 wakaba 1.136 my $char_onerror = sub {
602     my (undef, $type, %opt) = @_;
603 wakaba 1.153 !!!parse-error (layer => 'encode',
604 wakaba 1.174 line => $self->{line}, column => $self->{column} + 1,
605     %opt, type => $type);
606 wakaba 1.136 if ($opt{octets}) {
607     ${$opt{octets}} = "\x{FFFD}"; # relacement character
608     }
609     };
610 wakaba 1.162
611     my $wrapped_char_stream = $get_wrapper->($char_stream);
612     $wrapped_char_stream->onerror ($char_onerror);
613 wakaba 1.136
614 wakaba 1.182 my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615 wakaba 1.63 my $return;
616     try {
617 wakaba 1.162 $return = $self->parse_char_stream ($wrapped_char_stream, @args);
618 wakaba 1.63 } catch Whatpm::HTML::RestartParser with {
619 wakaba 1.134 ## NOTE: Invoked after {change_encoding}.
620    
621     if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622 wakaba 1.160 $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623 wakaba 1.153 !!!parse-error (type => 'chardecode:fallback',
624     level => $self->{level}->{uncertain},
625 wakaba 1.160 #text => $self->{input_encoding},
626 wakaba 1.153 line => 1, column => 1,
627     layer => 'encode');
628 wakaba 1.134 } elsif (not ($e_status &
629 wakaba 1.178 Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630 wakaba 1.160 $self->{input_encoding} = $charset->get_iana_name;
631 wakaba 1.153 !!!parse-error (type => 'chardecode:no error',
632     text => $self->{input_encoding},
633     level => $self->{level}->{uncertain},
634     line => 1, column => 1,
635     layer => 'encode');
636 wakaba 1.160 } else {
637     $self->{input_encoding} = $charset->get_iana_name;
638 wakaba 1.134 }
639 wakaba 1.63 $self->{confident} = 1;
640 wakaba 1.162
641     $wrapped_char_stream = $get_wrapper->($char_stream);
642     $wrapped_char_stream->onerror ($char_onerror);
643    
644     $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645 wakaba 1.63 };
646     return $return;
647 wakaba 1.138 } # parse_byte_stream
648 wakaba 1.63
649 wakaba 1.71 ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650     ## and the HTML layer MUST ignore it. However, we does strip BOM in
651     ## the encoding layer and the HTML layer does not ignore any U+FEFF,
652     ## because the core part of our HTML parser expects a string of character,
653     ## not a string of bytes or code units or anything which might contain a BOM.
654     ## Therefore, any parser interface that accepts a string of bytes,
655     ## such as |parse_byte_string| in this module, must ensure that it does
656     ## strip the BOM and never strip any ZWNBSP.
657    
658 wakaba 1.162 sub parse_char_string ($$$;$$) {
659     #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660 wakaba 1.135 my $self = shift;
661 wakaba 1.139 my $s = ref $_[0] ? $_[0] : \($_[0]);
662 wakaba 1.171 require Whatpm::Charset::DecodeHandle;
663     my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664 wakaba 1.135 return $self->parse_char_stream ($input, @_[1..$#_]);
665     } # parse_char_string
666 wakaba 1.162 *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667 wakaba 1.63
668 wakaba 1.182 sub parse_char_stream ($$$;$$) {
669 wakaba 1.63 my $self = ref $_[0] ? shift : shift->new;
670 wakaba 1.135 my $input = $_[0];
671 wakaba 1.1 $self->{document} = $_[1];
672 wakaba 1.63 @{$self->{document}->child_nodes} = ();
673 wakaba 1.1
674 wakaba 1.3 ## NOTE: |set_inner_html| copies most of this method's code
675    
676 wakaba 1.63 $self->{confident} = 1 unless exists $self->{confident};
677 wakaba 1.64 $self->{document}->input_encoding ($self->{input_encoding})
678     if defined $self->{input_encoding};
679 wakaba 1.178 ## TODO: |{input_encoding}| is needless?
680 wakaba 1.63
681 wakaba 1.112 $self->{line_prev} = $self->{line} = 1;
682 wakaba 1.179 $self->{column_prev} = -1;
683     $self->{column} = 0;
684 wakaba 1.183 $self->{set_nc} = sub {
685 wakaba 1.1 my $self = shift;
686 wakaba 1.13
687 wakaba 1.178 my $char = '';
688 wakaba 1.183 if (defined $self->{next_nc}) {
689     $char = $self->{next_nc};
690     delete $self->{next_nc};
691     $self->{nc} = ord $char;
692 wakaba 1.139 } else {
693 wakaba 1.179 $self->{char_buffer} = '';
694     $self->{char_buffer_pos} = 0;
695    
696     my $count = $input->manakai_read_until
697 wakaba 1.182 ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698 wakaba 1.179 if ($count) {
699     $self->{line_prev} = $self->{line};
700     $self->{column_prev} = $self->{column};
701     $self->{column}++;
702 wakaba 1.183 $self->{nc}
703 wakaba 1.179 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704     return;
705     }
706    
707 wakaba 1.178 if ($input->read ($char, 1)) {
708 wakaba 1.183 $self->{nc} = ord $char;
709 wakaba 1.178 } else {
710 wakaba 1.183 $self->{nc} = -1;
711 wakaba 1.178 return;
712     }
713 wakaba 1.139 }
714 wakaba 1.112
715     ($self->{line_prev}, $self->{column_prev})
716     = ($self->{line}, $self->{column});
717     $self->{column}++;
718 wakaba 1.1
719 wakaba 1.183 if ($self->{nc} == 0x000A) { # LF
720 wakaba 1.132 !!!cp ('j1');
721 wakaba 1.112 $self->{line}++;
722     $self->{column} = 0;
723 wakaba 1.183 } elsif ($self->{nc} == 0x000D) { # CR
724 wakaba 1.132 !!!cp ('j2');
725 wakaba 1.170 ## TODO: support for abort/streaming
726 wakaba 1.178 my $next = '';
727     if ($input->read ($next, 1) and $next ne "\x0A") {
728 wakaba 1.183 $self->{next_nc} = $next;
729 wakaba 1.135 }
730 wakaba 1.183 $self->{nc} = 0x000A; # LF # MUST
731 wakaba 1.112 $self->{line}++;
732     $self->{column} = 0;
733 wakaba 1.183 } elsif ($self->{nc} == 0x0000) { # NULL
734 wakaba 1.132 !!!cp ('j4');
735 wakaba 1.8 !!!parse-error (type => 'NULL');
736 wakaba 1.183 $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737 wakaba 1.1 }
738     };
739    
740 wakaba 1.172 $self->{read_until} = sub {
741     #my ($scalar, $specials_range, $offset) = @_;
742 wakaba 1.183 return 0 if defined $self->{next_nc};
743 wakaba 1.180
744 wakaba 1.182 my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745 wakaba 1.180 my $offset = $_[2] || 0;
746    
747     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748     pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749     if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750     substr ($_[0], $offset)
751     = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752     my $count = $+[0] - $-[0];
753     if ($count) {
754     $self->{column} += $count;
755     $self->{char_buffer_pos} += $count;
756     $self->{line_prev} = $self->{line};
757     $self->{column_prev} = $self->{column} - 1;
758 wakaba 1.183 $self->{nc} = -1;
759 wakaba 1.180 }
760     return $count;
761     } else {
762     return 0;
763     }
764     } else {
765     my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766     if ($count) {
767     $self->{column} += $count;
768     $self->{line_prev} = $self->{line};
769     $self->{column_prev} = $self->{column} - 1;
770 wakaba 1.183 $self->{nc} = -1;
771 wakaba 1.180 }
772     return $count;
773 wakaba 1.172 }
774     }; # $self->{read_until}
775 wakaba 1.171
776 wakaba 1.3 my $onerror = $_[2] || sub {
777     my (%opt) = @_;
778 wakaba 1.112 my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779     my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780     warn "Parse error ($opt{type}) at line $line column $column\n";
781 wakaba 1.3 };
782     $self->{parse_error} = sub {
783 wakaba 1.112 $onerror->(line => $self->{line}, column => $self->{column}, @_);
784 wakaba 1.1 };
785    
786 wakaba 1.182 my $char_onerror = sub {
787     my (undef, $type, %opt) = @_;
788     !!!parse-error (layer => 'encode',
789     line => $self->{line}, column => $self->{column} + 1,
790     %opt, type => $type);
791     }; # $char_onerror
792    
793     if ($_[3]) {
794     $input = $_[3]->($input);
795     $input->onerror ($char_onerror);
796     } else {
797     $input->onerror ($char_onerror) unless defined $input->onerror;
798     }
799    
800 wakaba 1.1 $self->_initialize_tokenizer;
801     $self->_initialize_tree_constructor;
802     $self->_construct_tree;
803     $self->_terminate_tree_constructor;
804    
805 wakaba 1.112 delete $self->{parse_error}; # remove loop
806    
807 wakaba 1.1 return $self->{document};
808 wakaba 1.135 } # parse_char_stream
809 wakaba 1.1
810     sub new ($) {
811     my $class = shift;
812 wakaba 1.134 my $self = bless {
813 wakaba 1.153 level => {must => 'm',
814 wakaba 1.159 should => 's',
815 wakaba 1.153 warn => 'w',
816     info => 'i',
817     uncertain => 'u'},
818 wakaba 1.134 }, $class;
819 wakaba 1.183 $self->{set_nc} = sub {
820     $self->{nc} = -1;
821 wakaba 1.1 };
822     $self->{parse_error} = sub {
823     #
824     };
825 wakaba 1.63 $self->{change_encoding} = sub {
826     # if ($_[0] is a supported encoding) {
827     # run "change the encoding" algorithm;
828     # throw Whatpm::HTML::RestartParser (charset => $new_encoding);
829     # }
830     };
831 wakaba 1.61 $self->{application_cache_selection} = sub {
832     #
833     };
834 wakaba 1.1 return $self;
835     } # new
836    
837 wakaba 1.40 sub CM_ENTITY () { 0b001 } # & markup in data
838     sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
839     sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
840    
841     sub PLAINTEXT_CONTENT_MODEL () { 0 }
842     sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
843     sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
844     sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846 wakaba 1.57 sub DATA_STATE () { 0 }
847 wakaba 1.168 #sub ENTITY_DATA_STATE () { 1 }
848 wakaba 1.57 sub TAG_OPEN_STATE () { 2 }
849     sub CLOSE_TAG_OPEN_STATE () { 3 }
850     sub TAG_NAME_STATE () { 4 }
851     sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
852     sub ATTRIBUTE_NAME_STATE () { 6 }
853     sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
854     sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
855     sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856     sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857     sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858 wakaba 1.168 #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859 wakaba 1.57 sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860     sub COMMENT_START_STATE () { 14 }
861     sub COMMENT_START_DASH_STATE () { 15 }
862     sub COMMENT_STATE () { 16 }
863     sub COMMENT_END_STATE () { 17 }
864     sub COMMENT_END_DASH_STATE () { 18 }
865     sub BOGUS_COMMENT_STATE () { 19 }
866     sub DOCTYPE_STATE () { 20 }
867     sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
868     sub DOCTYPE_NAME_STATE () { 22 }
869     sub AFTER_DOCTYPE_NAME_STATE () { 23 }
870     sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
871     sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
872     sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
873     sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
874     sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
875     sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
876     sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
877     sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878     sub BOGUS_DOCTYPE_STATE () { 32 }
879 wakaba 1.72 sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880 wakaba 1.125 sub SELF_CLOSING_START_TAG_STATE () { 34 }
881 wakaba 1.165 sub CDATA_SECTION_STATE () { 35 }
882 wakaba 1.164 sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883     sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884     sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885 wakaba 1.185 sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886 wakaba 1.165 sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887     sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888 wakaba 1.166 sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889     sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890 wakaba 1.168 ## NOTE: "Entity data state", "entity in attribute value state", and
891     ## "consume a character reference" algorithm are jointly implemented
892     ## using the following six states:
893     sub ENTITY_STATE () { 44 }
894     sub ENTITY_HASH_STATE () { 45 }
895     sub NCR_NUM_STATE () { 46 }
896     sub HEXREF_X_STATE () { 47 }
897     sub HEXREF_HEX_STATE () { 48 }
898     sub ENTITY_NAME_STATE () { 49 }
899 wakaba 1.185 sub PCDATA_STATE () { 50 } # "data state" in the spec
900 wakaba 1.57
901 wakaba 1.55 sub DOCTYPE_TOKEN () { 1 }
902     sub COMMENT_TOKEN () { 2 }
903     sub START_TAG_TOKEN () { 3 }
904     sub END_TAG_TOKEN () { 4 }
905     sub END_OF_FILE_TOKEN () { 5 }
906     sub CHARACTER_TOKEN () { 6 }
907    
908 wakaba 1.54 sub AFTER_HTML_IMS () { 0b100 }
909     sub HEAD_IMS () { 0b1000 }
910     sub BODY_IMS () { 0b10000 }
911 wakaba 1.56 sub BODY_TABLE_IMS () { 0b100000 }
912 wakaba 1.54 sub TABLE_IMS () { 0b1000000 }
913 wakaba 1.56 sub ROW_IMS () { 0b10000000 }
914 wakaba 1.54 sub BODY_AFTER_IMS () { 0b100000000 }
915     sub FRAME_IMS () { 0b1000000000 }
916 wakaba 1.101 sub SELECT_IMS () { 0b10000000000 }
917 wakaba 1.126 sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918     ## NOTE: "in foreign content" insertion mode is special; it is combined
919     ## with the secondary insertion mode. In this parser, they are stored
920     ## together in the bit-or'ed form.
921 wakaba 1.205 sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
922     ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
923     ## combined with the original insertion mode. In thie parser,
924     ## they are stored together in the bit-or'ed form.
925 wakaba 1.54
926 wakaba 1.84 ## NOTE: "initial" and "before html" insertion modes have no constants.
927    
928     ## NOTE: "after after body" insertion mode.
929 wakaba 1.54 sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
930 wakaba 1.84
931     ## NOTE: "after after frameset" insertion mode.
932 wakaba 1.54 sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
933 wakaba 1.84
934 wakaba 1.54 sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
935     sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
936     sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
937     sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
938     sub IN_BODY_IM () { BODY_IMS }
939 wakaba 1.56 sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
940     sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
941     sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
942     sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
943 wakaba 1.54 sub IN_TABLE_IM () { TABLE_IMS }
944     sub AFTER_BODY_IM () { BODY_AFTER_IMS }
945     sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
946     sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
947 wakaba 1.101 sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
948     sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
949 wakaba 1.54 sub IN_COLUMN_GROUP_IM () { 0b10 }
950    
951 wakaba 1.1 ## Implementations MUST act as if state machine in the spec
952    
953     sub _initialize_tokenizer ($) {
954     my $self = shift;
955 wakaba 1.57 $self->{state} = DATA_STATE; # MUST
956 wakaba 1.183 #$self->{s_kwd}; # state keyword - initialized when used
957 wakaba 1.169 #$self->{entity__value}; # initialized when used
958     #$self->{entity__match}; # initialized when used
959 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # be
960 wakaba 1.183 undef $self->{ct}; # current token
961     undef $self->{ca}; # current attribute
962     undef $self->{last_stag_name}; # last emitted start tag name
963 wakaba 1.169 #$self->{prev_state}; # initialized when used
964 wakaba 1.125 delete $self->{self_closing};
965 wakaba 1.179 $self->{char_buffer} = '';
966     $self->{char_buffer_pos} = 0;
967 wakaba 1.183 $self->{nc} = -1; # next input character
968     #$self->{next_nc}
969 wakaba 1.1 !!!next-input-character;
970     $self->{token} = [];
971 wakaba 1.18 # $self->{escape}
972 wakaba 1.1 } # _initialize_tokenizer
973    
974     ## A token has:
975 wakaba 1.55 ## ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
976     ## CHARACTER_TOKEN, or END_OF_FILE_TOKEN
977     ## ->{name} (DOCTYPE_TOKEN)
978     ## ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
979 wakaba 1.183 ## ->{pubid} (DOCTYPE_TOKEN)
980     ## ->{sysid} (DOCTYPE_TOKEN)
981 wakaba 1.75 ## ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
982 wakaba 1.55 ## ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
983 wakaba 1.66 ## ->{name}
984     ## ->{value}
985     ## ->{has_reference} == 1 or 0
986 wakaba 1.55 ## ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
987 wakaba 1.125 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
988     ## |->{self_closing}| is used to save the value of |$self->{self_closing}|
989     ## while the token is pushed back to the stack.
990    
991 wakaba 1.1 ## Emitted token MUST immediately be handled by the tree construction state.
992    
993     ## Before each step, UA MAY check to see if either one of the scripts in
994     ## "list of scripts that will execute as soon as possible" or the first
995     ## script in the "list of scripts that will execute asynchronously",
996     ## has completed loading. If one has, then it MUST be executed
997     ## and removed from the list.
998    
999 wakaba 1.169 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1000     ## (This requirement was dropped from HTML5 spec, unfortunately.)
1001 wakaba 1.59
1002 wakaba 1.187 my $is_space = {
1003     0x0009 => 1, # CHARACTER TABULATION (HT)
1004     0x000A => 1, # LINE FEED (LF)
1005     #0x000B => 0, # LINE TABULATION (VT)
1006     0x000C => 1, # FORM FEED (FF)
1007     #0x000D => 1, # CARRIAGE RETURN (CR)
1008     0x0020 => 1, # SPACE (SP)
1009     };
1010    
1011 wakaba 1.1 sub _get_next_token ($) {
1012     my $self = shift;
1013 wakaba 1.125
1014     if ($self->{self_closing}) {
1015 wakaba 1.183 !!!parse-error (type => 'nestc', token => $self->{ct});
1016 wakaba 1.125 ## NOTE: The |self_closing| flag is only set by start tag token.
1017     ## In addition, when a start tag token is emitted, it is always set to
1018 wakaba 1.183 ## |ct|.
1019 wakaba 1.125 delete $self->{self_closing};
1020     }
1021    
1022 wakaba 1.1 if (@{$self->{token}}) {
1023 wakaba 1.125 $self->{self_closing} = $self->{token}->[0]->{self_closing};
1024 wakaba 1.1 return shift @{$self->{token}};
1025     }
1026    
1027     A: {
1028 wakaba 1.185 if ($self->{state} == PCDATA_STATE) {
1029     ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1030    
1031 wakaba 1.183 if ($self->{nc} == 0x0026) { # &
1032 wakaba 1.185 !!!cp (0.1);
1033     ## NOTE: In the spec, the tokenizer is switched to the
1034     ## "entity data state". In this implementation, the tokenizer
1035     ## is switched to the |ENTITY_STATE|, which is an implementation
1036     ## of the "consume a character reference" algorithm.
1037     $self->{entity_add} = -1;
1038     $self->{prev_state} = DATA_STATE;
1039     $self->{state} = ENTITY_STATE;
1040     !!!next-input-character;
1041     redo A;
1042     } elsif ($self->{nc} == 0x003C) { # <
1043     !!!cp (0.2);
1044     $self->{state} = TAG_OPEN_STATE;
1045     !!!next-input-character;
1046     redo A;
1047     } elsif ($self->{nc} == -1) {
1048     !!!cp (0.3);
1049     !!!emit ({type => END_OF_FILE_TOKEN,
1050     line => $self->{line}, column => $self->{column}});
1051     last A; ## TODO: ok?
1052     } else {
1053     !!!cp (0.4);
1054     #
1055     }
1056    
1057     # Anything else
1058     my $token = {type => CHARACTER_TOKEN,
1059     data => chr $self->{nc},
1060     line => $self->{line}, column => $self->{column},
1061     };
1062     $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1063    
1064     ## Stay in the state.
1065     !!!next-input-character;
1066     !!!emit ($token);
1067     redo A;
1068     } elsif ($self->{state} == DATA_STATE) {
1069     $self->{s_kwd} = '' unless defined $self->{s_kwd};
1070     if ($self->{nc} == 0x0026) { # &
1071     $self->{s_kwd} = '';
1072 wakaba 1.72 if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1073     not $self->{escape}) {
1074 wakaba 1.77 !!!cp (1);
1075 wakaba 1.167 ## NOTE: In the spec, the tokenizer is switched to the
1076     ## "entity data state". In this implementation, the tokenizer
1077     ## is switched to the |ENTITY_STATE|, which is an implementation
1078     ## of the "consume a character reference" algorithm.
1079 wakaba 1.183 $self->{entity_add} = -1;
1080 wakaba 1.169 $self->{prev_state} = DATA_STATE;
1081 wakaba 1.167 $self->{state} = ENTITY_STATE;
1082 wakaba 1.1 !!!next-input-character;
1083     redo A;
1084     } else {
1085 wakaba 1.77 !!!cp (2);
1086 wakaba 1.1 #
1087     }
1088 wakaba 1.183 } elsif ($self->{nc} == 0x002D) { # -
1089 wakaba 1.40 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1090 wakaba 1.185 $self->{s_kwd} .= '-';
1091    
1092 wakaba 1.184 if ($self->{s_kwd} eq '<!--') {
1093     !!!cp (3);
1094     $self->{escape} = 1; # unless $self->{escape};
1095     $self->{s_kwd} = '--';
1096     #
1097     } elsif ($self->{s_kwd} eq '---') {
1098     !!!cp (4);
1099     $self->{s_kwd} = '--';
1100     #
1101 wakaba 1.77 } else {
1102     !!!cp (5);
1103 wakaba 1.184 #
1104 wakaba 1.13 }
1105     }
1106    
1107     #
1108 wakaba 1.184 } elsif ($self->{nc} == 0x0021) { # !
1109 wakaba 1.185 if (length $self->{s_kwd}) {
1110 wakaba 1.184 !!!cp (5.1);
1111     $self->{s_kwd} .= '!';
1112     #
1113     } else {
1114     !!!cp (5.2);
1115 wakaba 1.185 #$self->{s_kwd} = '';
1116 wakaba 1.184 #
1117     }
1118     #
1119 wakaba 1.183 } elsif ($self->{nc} == 0x003C) { # <
1120 wakaba 1.40 if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1121     (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1122 wakaba 1.13 not $self->{escape})) {
1123 wakaba 1.77 !!!cp (6);
1124 wakaba 1.57 $self->{state} = TAG_OPEN_STATE;
1125 wakaba 1.1 !!!next-input-character;
1126     redo A;
1127     } else {
1128 wakaba 1.77 !!!cp (7);
1129 wakaba 1.185 $self->{s_kwd} = '';
1130 wakaba 1.1 #
1131     }
1132 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1133 wakaba 1.13 if ($self->{escape} and
1134 wakaba 1.40 ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1135 wakaba 1.185 if ($self->{s_kwd} eq '--') {
1136 wakaba 1.77 !!!cp (8);
1137 wakaba 1.13 delete $self->{escape};
1138 wakaba 1.77 } else {
1139     !!!cp (9);
1140 wakaba 1.13 }
1141 wakaba 1.77 } else {
1142     !!!cp (10);
1143 wakaba 1.13 }
1144    
1145 wakaba 1.185 $self->{s_kwd} = '';
1146 wakaba 1.13 #
1147 wakaba 1.183 } elsif ($self->{nc} == -1) {
1148 wakaba 1.77 !!!cp (11);
1149 wakaba 1.185 $self->{s_kwd} = '';
1150 wakaba 1.112 !!!emit ({type => END_OF_FILE_TOKEN,
1151     line => $self->{line}, column => $self->{column}});
1152 wakaba 1.1 last A; ## TODO: ok?
1153 wakaba 1.77 } else {
1154     !!!cp (12);
1155 wakaba 1.185 $self->{s_kwd} = '';
1156 wakaba 1.184 #
1157 wakaba 1.1 }
1158 wakaba 1.184
1159 wakaba 1.1 # Anything else
1160 wakaba 1.55 my $token = {type => CHARACTER_TOKEN,
1161 wakaba 1.183 data => chr $self->{nc},
1162 wakaba 1.120 line => $self->{line}, column => $self->{column},
1163 wakaba 1.118 };
1164 wakaba 1.184 if ($self->{read_until}->($token->{data}, q[-!<>&],
1165     length $token->{data})) {
1166 wakaba 1.185 $self->{s_kwd} = '';
1167 wakaba 1.184 }
1168 wakaba 1.171
1169 wakaba 1.185 ## Stay in the data state.
1170     if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1171     !!!cp (13);
1172     $self->{state} = PCDATA_STATE;
1173     } else {
1174     !!!cp (14);
1175     ## Stay in the state.
1176     }
1177 wakaba 1.1 !!!next-input-character;
1178     !!!emit ($token);
1179     redo A;
1180 wakaba 1.57 } elsif ($self->{state} == TAG_OPEN_STATE) {
1181 wakaba 1.40 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1182 wakaba 1.183 if ($self->{nc} == 0x002F) { # /
1183 wakaba 1.77 !!!cp (15);
1184 wakaba 1.1 !!!next-input-character;
1185 wakaba 1.57 $self->{state} = CLOSE_TAG_OPEN_STATE;
1186 wakaba 1.1 redo A;
1187 wakaba 1.184 } elsif ($self->{nc} == 0x0021) { # !
1188     !!!cp (15.1);
1189     $self->{s_kwd} = '<' unless $self->{escape};
1190     #
1191 wakaba 1.1 } else {
1192 wakaba 1.77 !!!cp (16);
1193 wakaba 1.184 #
1194     }
1195 wakaba 1.1
1196 wakaba 1.184 ## reconsume
1197     $self->{state} = DATA_STATE;
1198     !!!emit ({type => CHARACTER_TOKEN, data => '<',
1199     line => $self->{line_prev},
1200     column => $self->{column_prev},
1201     });
1202     redo A;
1203 wakaba 1.40 } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1204 wakaba 1.183 if ($self->{nc} == 0x0021) { # !
1205 wakaba 1.77 !!!cp (17);
1206 wakaba 1.57 $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1207 wakaba 1.1 !!!next-input-character;
1208     redo A;
1209 wakaba 1.183 } elsif ($self->{nc} == 0x002F) { # /
1210 wakaba 1.77 !!!cp (18);
1211 wakaba 1.57 $self->{state} = CLOSE_TAG_OPEN_STATE;
1212 wakaba 1.1 !!!next-input-character;
1213     redo A;
1214 wakaba 1.183 } elsif (0x0041 <= $self->{nc} and
1215     $self->{nc} <= 0x005A) { # A..Z
1216 wakaba 1.77 !!!cp (19);
1217 wakaba 1.183 $self->{ct}
1218 wakaba 1.55 = {type => START_TAG_TOKEN,
1219 wakaba 1.183 tag_name => chr ($self->{nc} + 0x0020),
1220 wakaba 1.112 line => $self->{line_prev},
1221     column => $self->{column_prev}};
1222 wakaba 1.57 $self->{state} = TAG_NAME_STATE;
1223 wakaba 1.1 !!!next-input-character;
1224     redo A;
1225 wakaba 1.183 } elsif (0x0061 <= $self->{nc} and
1226     $self->{nc} <= 0x007A) { # a..z
1227 wakaba 1.77 !!!cp (20);
1228 wakaba 1.183 $self->{ct} = {type => START_TAG_TOKEN,
1229     tag_name => chr ($self->{nc}),
1230 wakaba 1.112 line => $self->{line_prev},
1231     column => $self->{column_prev}};
1232 wakaba 1.57 $self->{state} = TAG_NAME_STATE;
1233 wakaba 1.1 !!!next-input-character;
1234     redo A;
1235 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1236 wakaba 1.77 !!!cp (21);
1237 wakaba 1.115 !!!parse-error (type => 'empty start tag',
1238     line => $self->{line_prev},
1239     column => $self->{column_prev});
1240 wakaba 1.57 $self->{state} = DATA_STATE;
1241 wakaba 1.1 !!!next-input-character;
1242    
1243 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1244 wakaba 1.120 line => $self->{line_prev},
1245     column => $self->{column_prev},
1246 wakaba 1.118 });
1247 wakaba 1.1
1248     redo A;
1249 wakaba 1.183 } elsif ($self->{nc} == 0x003F) { # ?
1250 wakaba 1.77 !!!cp (22);
1251 wakaba 1.115 !!!parse-error (type => 'pio',
1252     line => $self->{line_prev},
1253     column => $self->{column_prev});
1254 wakaba 1.57 $self->{state} = BOGUS_COMMENT_STATE;
1255 wakaba 1.183 $self->{ct} = {type => COMMENT_TOKEN, data => '',
1256 wakaba 1.120 line => $self->{line_prev},
1257     column => $self->{column_prev},
1258 wakaba 1.118 };
1259 wakaba 1.183 ## $self->{nc} is intentionally left as is
1260 wakaba 1.1 redo A;
1261     } else {
1262 wakaba 1.77 !!!cp (23);
1263 wakaba 1.136 !!!parse-error (type => 'bare stago',
1264     line => $self->{line_prev},
1265     column => $self->{column_prev});
1266 wakaba 1.57 $self->{state} = DATA_STATE;
1267 wakaba 1.1 ## reconsume
1268    
1269 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '<',
1270 wakaba 1.120 line => $self->{line_prev},
1271     column => $self->{column_prev},
1272 wakaba 1.118 });
1273 wakaba 1.1
1274     redo A;
1275     }
1276     } else {
1277 wakaba 1.40 die "$0: $self->{content_model} in tag open";
1278 wakaba 1.1 }
1279 wakaba 1.57 } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1280 wakaba 1.164 ## NOTE: The "close tag open state" in the spec is implemented as
1281 wakaba 1.185 ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1282 wakaba 1.164
1283 wakaba 1.113 my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1284 wakaba 1.40 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1285 wakaba 1.183 if (defined $self->{last_stag_name}) {
1286 wakaba 1.185 $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1287 wakaba 1.183 $self->{s_kwd} = '';
1288 wakaba 1.164 ## Reconsume.
1289     redo A;
1290 wakaba 1.23 } else {
1291     ## No start tag token has ever been emitted
1292 wakaba 1.164 ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1293 wakaba 1.77 !!!cp (28);
1294 wakaba 1.57 $self->{state} = DATA_STATE;
1295 wakaba 1.164 ## Reconsume.
1296 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '</',
1297 wakaba 1.120 line => $l, column => $c,
1298 wakaba 1.118 });
1299 wakaba 1.1 redo A;
1300     }
1301     }
1302 wakaba 1.164
1303 wakaba 1.183 if (0x0041 <= $self->{nc} and
1304     $self->{nc} <= 0x005A) { # A..Z
1305 wakaba 1.77 !!!cp (29);
1306 wakaba 1.183 $self->{ct}
1307 wakaba 1.112 = {type => END_TAG_TOKEN,
1308 wakaba 1.183 tag_name => chr ($self->{nc} + 0x0020),
1309 wakaba 1.112 line => $l, column => $c};
1310 wakaba 1.57 $self->{state} = TAG_NAME_STATE;
1311 wakaba 1.1 !!!next-input-character;
1312     redo A;
1313 wakaba 1.183 } elsif (0x0061 <= $self->{nc} and
1314     $self->{nc} <= 0x007A) { # a..z
1315 wakaba 1.77 !!!cp (30);
1316 wakaba 1.183 $self->{ct} = {type => END_TAG_TOKEN,
1317     tag_name => chr ($self->{nc}),
1318 wakaba 1.112 line => $l, column => $c};
1319 wakaba 1.57 $self->{state} = TAG_NAME_STATE;
1320 wakaba 1.1 !!!next-input-character;
1321     redo A;
1322 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1323 wakaba 1.77 !!!cp (31);
1324 wakaba 1.115 !!!parse-error (type => 'empty end tag',
1325     line => $self->{line_prev}, ## "<" in "</>"
1326     column => $self->{column_prev} - 1);
1327 wakaba 1.57 $self->{state} = DATA_STATE;
1328 wakaba 1.1 !!!next-input-character;
1329     redo A;
1330 wakaba 1.183 } elsif ($self->{nc} == -1) {
1331 wakaba 1.77 !!!cp (32);
1332 wakaba 1.3 !!!parse-error (type => 'bare etago');
1333 wakaba 1.57 $self->{state} = DATA_STATE;
1334 wakaba 1.1 # reconsume
1335    
1336 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '</',
1337 wakaba 1.120 line => $l, column => $c,
1338 wakaba 1.118 });
1339 wakaba 1.1
1340     redo A;
1341     } else {
1342 wakaba 1.77 !!!cp (33);
1343 wakaba 1.3 !!!parse-error (type => 'bogus end tag');
1344 wakaba 1.57 $self->{state} = BOGUS_COMMENT_STATE;
1345 wakaba 1.183 $self->{ct} = {type => COMMENT_TOKEN, data => '',
1346 wakaba 1.120 line => $self->{line_prev}, # "<" of "</"
1347     column => $self->{column_prev} - 1,
1348 wakaba 1.118 };
1349 wakaba 1.183 ## NOTE: $self->{nc} is intentionally left as is.
1350 wakaba 1.164 ## Although the "anything else" case of the spec not explicitly
1351     ## states that the next input character is to be reconsumed,
1352     ## it will be included to the |data| of the comment token
1353     ## generated from the bogus end tag, as defined in the
1354     ## "bogus comment state" entry.
1355     redo A;
1356     }
1357 wakaba 1.185 } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1358 wakaba 1.183 my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1359 wakaba 1.164 if (length $ch) {
1360     my $CH = $ch;
1361     $ch =~ tr/a-z/A-Z/;
1362 wakaba 1.183 my $nch = chr $self->{nc};
1363 wakaba 1.164 if ($nch eq $ch or $nch eq $CH) {
1364     !!!cp (24);
1365     ## Stay in the state.
1366 wakaba 1.183 $self->{s_kwd} .= $nch;
1367 wakaba 1.164 !!!next-input-character;
1368     redo A;
1369     } else {
1370     !!!cp (25);
1371     $self->{state} = DATA_STATE;
1372     ## Reconsume.
1373     !!!emit ({type => CHARACTER_TOKEN,
1374 wakaba 1.183 data => '</' . $self->{s_kwd},
1375 wakaba 1.164 line => $self->{line_prev},
1376 wakaba 1.183 column => $self->{column_prev} - 1 - length $self->{s_kwd},
1377 wakaba 1.164 });
1378     redo A;
1379     }
1380     } else { # after "<{tag-name}"
1381 wakaba 1.187 unless ($is_space->{$self->{nc}} or
1382     {
1383 wakaba 1.164 0x003E => 1, # >
1384     0x002F => 1, # /
1385     -1 => 1, # EOF
1386 wakaba 1.183 }->{$self->{nc}}) {
1387 wakaba 1.164 !!!cp (26);
1388     ## Reconsume.
1389     $self->{state} = DATA_STATE;
1390     !!!emit ({type => CHARACTER_TOKEN,
1391 wakaba 1.183 data => '</' . $self->{s_kwd},
1392 wakaba 1.164 line => $self->{line_prev},
1393 wakaba 1.183 column => $self->{column_prev} - 1 - length $self->{s_kwd},
1394 wakaba 1.164 });
1395     redo A;
1396     } else {
1397     !!!cp (27);
1398 wakaba 1.183 $self->{ct}
1399 wakaba 1.164 = {type => END_TAG_TOKEN,
1400 wakaba 1.183 tag_name => $self->{last_stag_name},
1401 wakaba 1.164 line => $self->{line_prev},
1402 wakaba 1.183 column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1403 wakaba 1.164 $self->{state} = TAG_NAME_STATE;
1404     ## Reconsume.
1405     redo A;
1406     }
1407 wakaba 1.1 }
1408 wakaba 1.57 } elsif ($self->{state} == TAG_NAME_STATE) {
1409 wakaba 1.187 if ($is_space->{$self->{nc}}) {
1410 wakaba 1.77 !!!cp (34);
1411 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1412 wakaba 1.1 !!!next-input-character;
1413     redo A;
1414 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1415     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1416 wakaba 1.77 !!!cp (35);
1417 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1418     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1419 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1420 wakaba 1.183 #if ($self->{ct}->{attributes}) {
1421 wakaba 1.78 # ## NOTE: This should never be reached.
1422     # !!! cp (36);
1423     # !!! parse-error (type => 'end tag attribute');
1424     #} else {
1425 wakaba 1.77 !!!cp (37);
1426 wakaba 1.78 #}
1427 wakaba 1.1 } else {
1428 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1429 wakaba 1.1 }
1430 wakaba 1.57 $self->{state} = DATA_STATE;
1431 wakaba 1.1 !!!next-input-character;
1432    
1433 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1434 wakaba 1.1
1435     redo A;
1436 wakaba 1.183 } elsif (0x0041 <= $self->{nc} and
1437     $self->{nc} <= 0x005A) { # A..Z
1438 wakaba 1.77 !!!cp (38);
1439 wakaba 1.183 $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1440 wakaba 1.1 # start tag or end tag
1441     ## Stay in this state
1442     !!!next-input-character;
1443     redo A;
1444 wakaba 1.183 } elsif ($self->{nc} == -1) {
1445 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1446 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1447 wakaba 1.77 !!!cp (39);
1448 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1449     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1450 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1451 wakaba 1.183 #if ($self->{ct}->{attributes}) {
1452 wakaba 1.78 # ## NOTE: This state should never be reached.
1453     # !!! cp (40);
1454     # !!! parse-error (type => 'end tag attribute');
1455     #} else {
1456 wakaba 1.77 !!!cp (41);
1457 wakaba 1.78 #}
1458 wakaba 1.1 } else {
1459 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1460 wakaba 1.1 }
1461 wakaba 1.57 $self->{state} = DATA_STATE;
1462 wakaba 1.1 # reconsume
1463    
1464 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1465 wakaba 1.1
1466     redo A;
1467 wakaba 1.183 } elsif ($self->{nc} == 0x002F) { # /
1468 wakaba 1.125 !!!cp (42);
1469     $self->{state} = SELF_CLOSING_START_TAG_STATE;
1470 wakaba 1.1 !!!next-input-character;
1471     redo A;
1472     } else {
1473 wakaba 1.77 !!!cp (44);
1474 wakaba 1.183 $self->{ct}->{tag_name} .= chr $self->{nc};
1475 wakaba 1.1 # start tag or end tag
1476     ## Stay in the state
1477     !!!next-input-character;
1478     redo A;
1479     }
1480 wakaba 1.57 } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1481 wakaba 1.187 if ($is_space->{$self->{nc}}) {
1482 wakaba 1.77 !!!cp (45);
1483 wakaba 1.1 ## Stay in the state
1484     !!!next-input-character;
1485     redo A;
1486 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1487     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1488 wakaba 1.77 !!!cp (46);
1489 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1490     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1491 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1492 wakaba 1.183 if ($self->{ct}->{attributes}) {
1493 wakaba 1.77 !!!cp (47);
1494 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1495 wakaba 1.77 } else {
1496     !!!cp (48);
1497 wakaba 1.1 }
1498     } else {
1499 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1500 wakaba 1.1 }
1501 wakaba 1.57 $self->{state} = DATA_STATE;
1502 wakaba 1.1 !!!next-input-character;
1503    
1504 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1505 wakaba 1.1
1506     redo A;
1507 wakaba 1.183 } elsif (0x0041 <= $self->{nc} and
1508     $self->{nc} <= 0x005A) { # A..Z
1509 wakaba 1.77 !!!cp (49);
1510 wakaba 1.183 $self->{ca}
1511     = {name => chr ($self->{nc} + 0x0020),
1512 wakaba 1.119 value => '',
1513     line => $self->{line}, column => $self->{column}};
1514 wakaba 1.57 $self->{state} = ATTRIBUTE_NAME_STATE;
1515 wakaba 1.1 !!!next-input-character;
1516     redo A;
1517 wakaba 1.183 } elsif ($self->{nc} == 0x002F) { # /
1518 wakaba 1.125 !!!cp (50);
1519     $self->{state} = SELF_CLOSING_START_TAG_STATE;
1520 wakaba 1.1 !!!next-input-character;
1521     redo A;
1522 wakaba 1.183 } elsif ($self->{nc} == -1) {
1523 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1524 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1525 wakaba 1.77 !!!cp (52);
1526 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1527     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1528 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1529 wakaba 1.183 if ($self->{ct}->{attributes}) {
1530 wakaba 1.77 !!!cp (53);
1531 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1532 wakaba 1.77 } else {
1533     !!!cp (54);
1534 wakaba 1.1 }
1535     } else {
1536 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1537 wakaba 1.1 }
1538 wakaba 1.57 $self->{state} = DATA_STATE;
1539 wakaba 1.1 # reconsume
1540    
1541 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1542 wakaba 1.1
1543     redo A;
1544     } else {
1545 wakaba 1.72 if ({
1546     0x0022 => 1, # "
1547     0x0027 => 1, # '
1548     0x003D => 1, # =
1549 wakaba 1.183 }->{$self->{nc}}) {
1550 wakaba 1.77 !!!cp (55);
1551 wakaba 1.72 !!!parse-error (type => 'bad attribute name');
1552 wakaba 1.77 } else {
1553     !!!cp (56);
1554 wakaba 1.72 }
1555 wakaba 1.183 $self->{ca}
1556     = {name => chr ($self->{nc}),
1557 wakaba 1.119 value => '',
1558     line => $self->{line}, column => $self->{column}};
1559 wakaba 1.57 $self->{state} = ATTRIBUTE_NAME_STATE;
1560 wakaba 1.1 !!!next-input-character;
1561     redo A;
1562     }
1563 wakaba 1.57 } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1564 wakaba 1.1 my $before_leave = sub {
1565 wakaba 1.183 if (exists $self->{ct}->{attributes} # start tag or end tag
1566     ->{$self->{ca}->{name}}) { # MUST
1567 wakaba 1.77 !!!cp (57);
1568 wakaba 1.183 !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1569     ## Discard $self->{ca} # MUST
1570 wakaba 1.1 } else {
1571 wakaba 1.77 !!!cp (58);
1572 wakaba 1.183 $self->{ct}->{attributes}->{$self->{ca}->{name}}
1573     = $self->{ca};
1574 wakaba 1.1 }
1575     }; # $before_leave
1576    
1577 wakaba 1.187 if ($is_space->{$self->{nc}}) {
1578 wakaba 1.77 !!!cp (59);
1579 wakaba 1.1 $before_leave->();
1580 wakaba 1.57 $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1581 wakaba 1.1 !!!next-input-character;
1582     redo A;
1583 wakaba 1.183 } elsif ($self->{nc} == 0x003D) { # =
1584 wakaba 1.77 !!!cp (60);
1585 wakaba 1.1 $before_leave->();
1586 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1587 wakaba 1.1 !!!next-input-character;
1588     redo A;
1589 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1590 wakaba 1.1 $before_leave->();
1591 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1592 wakaba 1.77 !!!cp (61);
1593 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1594     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1595 wakaba 1.77 !!!cp (62);
1596 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1597 wakaba 1.183 if ($self->{ct}->{attributes}) {
1598 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1599 wakaba 1.1 }
1600     } else {
1601 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1602 wakaba 1.1 }
1603 wakaba 1.57 $self->{state} = DATA_STATE;
1604 wakaba 1.1 !!!next-input-character;
1605    
1606 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1607 wakaba 1.1
1608     redo A;
1609 wakaba 1.183 } elsif (0x0041 <= $self->{nc} and
1610     $self->{nc} <= 0x005A) { # A..Z
1611 wakaba 1.77 !!!cp (63);
1612 wakaba 1.183 $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1613 wakaba 1.1 ## Stay in the state
1614     !!!next-input-character;
1615     redo A;
1616 wakaba 1.183 } elsif ($self->{nc} == 0x002F) { # /
1617 wakaba 1.125 !!!cp (64);
1618 wakaba 1.1 $before_leave->();
1619 wakaba 1.125 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1620 wakaba 1.1 !!!next-input-character;
1621     redo A;
1622 wakaba 1.183 } elsif ($self->{nc} == -1) {
1623 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1624 wakaba 1.1 $before_leave->();
1625 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1626 wakaba 1.77 !!!cp (66);
1627 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1628     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1629 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1630 wakaba 1.183 if ($self->{ct}->{attributes}) {
1631 wakaba 1.77 !!!cp (67);
1632 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1633 wakaba 1.77 } else {
1634 wakaba 1.78 ## NOTE: This state should never be reached.
1635 wakaba 1.77 !!!cp (68);
1636 wakaba 1.1 }
1637     } else {
1638 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1639 wakaba 1.1 }
1640 wakaba 1.57 $self->{state} = DATA_STATE;
1641 wakaba 1.1 # reconsume
1642    
1643 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1644 wakaba 1.1
1645     redo A;
1646     } else {
1647 wakaba 1.183 if ($self->{nc} == 0x0022 or # "
1648     $self->{nc} == 0x0027) { # '
1649 wakaba 1.77 !!!cp (69);
1650 wakaba 1.72 !!!parse-error (type => 'bad attribute name');
1651 wakaba 1.77 } else {
1652     !!!cp (70);
1653 wakaba 1.72 }
1654 wakaba 1.183 $self->{ca}->{name} .= chr ($self->{nc});
1655 wakaba 1.1 ## Stay in the state
1656     !!!next-input-character;
1657     redo A;
1658     }
1659 wakaba 1.57 } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1660 wakaba 1.187 if ($is_space->{$self->{nc}}) {
1661 wakaba 1.77 !!!cp (71);
1662 wakaba 1.1 ## Stay in the state
1663     !!!next-input-character;
1664     redo A;
1665 wakaba 1.183 } elsif ($self->{nc} == 0x003D) { # =
1666 wakaba 1.77 !!!cp (72);
1667 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1668 wakaba 1.1 !!!next-input-character;
1669     redo A;
1670 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1671     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1672 wakaba 1.77 !!!cp (73);
1673 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1674     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1675 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1676 wakaba 1.183 if ($self->{ct}->{attributes}) {
1677 wakaba 1.77 !!!cp (74);
1678 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1679 wakaba 1.77 } else {
1680 wakaba 1.78 ## NOTE: This state should never be reached.
1681 wakaba 1.77 !!!cp (75);
1682 wakaba 1.1 }
1683     } else {
1684 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1685 wakaba 1.1 }
1686 wakaba 1.57 $self->{state} = DATA_STATE;
1687 wakaba 1.1 !!!next-input-character;
1688    
1689 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1690 wakaba 1.1
1691     redo A;
1692 wakaba 1.183 } elsif (0x0041 <= $self->{nc} and
1693     $self->{nc} <= 0x005A) { # A..Z
1694 wakaba 1.77 !!!cp (76);
1695 wakaba 1.183 $self->{ca}
1696     = {name => chr ($self->{nc} + 0x0020),
1697 wakaba 1.119 value => '',
1698     line => $self->{line}, column => $self->{column}};
1699 wakaba 1.57 $self->{state} = ATTRIBUTE_NAME_STATE;
1700 wakaba 1.1 !!!next-input-character;
1701     redo A;
1702 wakaba 1.183 } elsif ($self->{nc} == 0x002F) { # /
1703 wakaba 1.125 !!!cp (77);
1704     $self->{state} = SELF_CLOSING_START_TAG_STATE;
1705 wakaba 1.1 !!!next-input-character;
1706     redo A;
1707 wakaba 1.183 } elsif ($self->{nc} == -1) {
1708 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1709 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1710 wakaba 1.77 !!!cp (79);
1711 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1712     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1713 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1714 wakaba 1.183 if ($self->{ct}->{attributes}) {
1715 wakaba 1.77 !!!cp (80);
1716 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1717 wakaba 1.77 } else {
1718 wakaba 1.78 ## NOTE: This state should never be reached.
1719 wakaba 1.77 !!!cp (81);
1720 wakaba 1.1 }
1721     } else {
1722 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1723 wakaba 1.1 }
1724 wakaba 1.57 $self->{state} = DATA_STATE;
1725 wakaba 1.1 # reconsume
1726    
1727 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1728 wakaba 1.1
1729     redo A;
1730     } else {
1731 wakaba 1.183 if ($self->{nc} == 0x0022 or # "
1732     $self->{nc} == 0x0027) { # '
1733 wakaba 1.156 !!!cp (78);
1734     !!!parse-error (type => 'bad attribute name');
1735     } else {
1736     !!!cp (82);
1737     }
1738 wakaba 1.183 $self->{ca}
1739     = {name => chr ($self->{nc}),
1740 wakaba 1.119 value => '',
1741     line => $self->{line}, column => $self->{column}};
1742 wakaba 1.57 $self->{state} = ATTRIBUTE_NAME_STATE;
1743 wakaba 1.1 !!!next-input-character;
1744     redo A;
1745     }
1746 wakaba 1.57 } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1747 wakaba 1.187 if ($is_space->{$self->{nc}}) {
1748 wakaba 1.77 !!!cp (83);
1749 wakaba 1.1 ## Stay in the state
1750     !!!next-input-character;
1751     redo A;
1752 wakaba 1.183 } elsif ($self->{nc} == 0x0022) { # "
1753 wakaba 1.77 !!!cp (84);
1754 wakaba 1.57 $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1755 wakaba 1.1 !!!next-input-character;
1756     redo A;
1757 wakaba 1.183 } elsif ($self->{nc} == 0x0026) { # &
1758 wakaba 1.77 !!!cp (85);
1759 wakaba 1.57 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1760 wakaba 1.1 ## reconsume
1761     redo A;
1762 wakaba 1.183 } elsif ($self->{nc} == 0x0027) { # '
1763 wakaba 1.77 !!!cp (86);
1764 wakaba 1.57 $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1765 wakaba 1.1 !!!next-input-character;
1766     redo A;
1767 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1768 wakaba 1.156 !!!parse-error (type => 'empty unquoted attribute value');
1769 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1770 wakaba 1.77 !!!cp (87);
1771 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1772     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1773 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1774 wakaba 1.183 if ($self->{ct}->{attributes}) {
1775 wakaba 1.77 !!!cp (88);
1776 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1777 wakaba 1.77 } else {
1778 wakaba 1.78 ## NOTE: This state should never be reached.
1779 wakaba 1.77 !!!cp (89);
1780 wakaba 1.1 }
1781     } else {
1782 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1783 wakaba 1.1 }
1784 wakaba 1.57 $self->{state} = DATA_STATE;
1785 wakaba 1.1 !!!next-input-character;
1786    
1787 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1788 wakaba 1.1
1789     redo A;
1790 wakaba 1.183 } elsif ($self->{nc} == -1) {
1791 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1792 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1793 wakaba 1.77 !!!cp (90);
1794 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1795     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1796 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1797 wakaba 1.183 if ($self->{ct}->{attributes}) {
1798 wakaba 1.77 !!!cp (91);
1799 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1800 wakaba 1.77 } else {
1801 wakaba 1.78 ## NOTE: This state should never be reached.
1802 wakaba 1.77 !!!cp (92);
1803 wakaba 1.1 }
1804     } else {
1805 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1806 wakaba 1.1 }
1807 wakaba 1.57 $self->{state} = DATA_STATE;
1808 wakaba 1.1 ## reconsume
1809    
1810 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1811 wakaba 1.1
1812     redo A;
1813     } else {
1814 wakaba 1.183 if ($self->{nc} == 0x003D) { # =
1815 wakaba 1.77 !!!cp (93);
1816 wakaba 1.72 !!!parse-error (type => 'bad attribute value');
1817 wakaba 1.77 } else {
1818     !!!cp (94);
1819 wakaba 1.72 }
1820 wakaba 1.183 $self->{ca}->{value} .= chr ($self->{nc});
1821 wakaba 1.57 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1822 wakaba 1.1 !!!next-input-character;
1823     redo A;
1824     }
1825 wakaba 1.57 } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1826 wakaba 1.183 if ($self->{nc} == 0x0022) { # "
1827 wakaba 1.77 !!!cp (95);
1828 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1829 wakaba 1.1 !!!next-input-character;
1830     redo A;
1831 wakaba 1.183 } elsif ($self->{nc} == 0x0026) { # &
1832 wakaba 1.77 !!!cp (96);
1833 wakaba 1.167 ## NOTE: In the spec, the tokenizer is switched to the
1834     ## "entity in attribute value state". In this implementation, the
1835     ## tokenizer is switched to the |ENTITY_STATE|, which is an
1836     ## implementation of the "consume a character reference" algorithm.
1837 wakaba 1.169 $self->{prev_state} = $self->{state};
1838 wakaba 1.183 $self->{entity_add} = 0x0022; # "
1839 wakaba 1.167 $self->{state} = ENTITY_STATE;
1840 wakaba 1.1 !!!next-input-character;
1841     redo A;
1842 wakaba 1.183 } elsif ($self->{nc} == -1) {
1843 wakaba 1.3 !!!parse-error (type => 'unclosed attribute value');
1844 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1845 wakaba 1.77 !!!cp (97);
1846 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1847     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1848 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1849 wakaba 1.183 if ($self->{ct}->{attributes}) {
1850 wakaba 1.77 !!!cp (98);
1851 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1852 wakaba 1.77 } else {
1853 wakaba 1.78 ## NOTE: This state should never be reached.
1854 wakaba 1.77 !!!cp (99);
1855 wakaba 1.1 }
1856     } else {
1857 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1858 wakaba 1.1 }
1859 wakaba 1.57 $self->{state} = DATA_STATE;
1860 wakaba 1.1 ## reconsume
1861    
1862 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1863 wakaba 1.1
1864     redo A;
1865     } else {
1866 wakaba 1.77 !!!cp (100);
1867 wakaba 1.183 $self->{ca}->{value} .= chr ($self->{nc});
1868     $self->{read_until}->($self->{ca}->{value},
1869 wakaba 1.173 q["&],
1870 wakaba 1.183 length $self->{ca}->{value});
1871 wakaba 1.173
1872 wakaba 1.1 ## Stay in the state
1873     !!!next-input-character;
1874     redo A;
1875     }
1876 wakaba 1.57 } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1877 wakaba 1.183 if ($self->{nc} == 0x0027) { # '
1878 wakaba 1.77 !!!cp (101);
1879 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1880 wakaba 1.1 !!!next-input-character;
1881     redo A;
1882 wakaba 1.183 } elsif ($self->{nc} == 0x0026) { # &
1883 wakaba 1.77 !!!cp (102);
1884 wakaba 1.167 ## NOTE: In the spec, the tokenizer is switched to the
1885     ## "entity in attribute value state". In this implementation, the
1886     ## tokenizer is switched to the |ENTITY_STATE|, which is an
1887     ## implementation of the "consume a character reference" algorithm.
1888 wakaba 1.183 $self->{entity_add} = 0x0027; # '
1889 wakaba 1.169 $self->{prev_state} = $self->{state};
1890 wakaba 1.167 $self->{state} = ENTITY_STATE;
1891 wakaba 1.1 !!!next-input-character;
1892     redo A;
1893 wakaba 1.183 } elsif ($self->{nc} == -1) {
1894 wakaba 1.3 !!!parse-error (type => 'unclosed attribute value');
1895 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1896 wakaba 1.77 !!!cp (103);
1897 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1898     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1899 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1900 wakaba 1.183 if ($self->{ct}->{attributes}) {
1901 wakaba 1.77 !!!cp (104);
1902 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1903 wakaba 1.77 } else {
1904 wakaba 1.78 ## NOTE: This state should never be reached.
1905 wakaba 1.77 !!!cp (105);
1906 wakaba 1.1 }
1907     } else {
1908 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1909 wakaba 1.1 }
1910 wakaba 1.57 $self->{state} = DATA_STATE;
1911 wakaba 1.1 ## reconsume
1912    
1913 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1914 wakaba 1.1
1915     redo A;
1916     } else {
1917 wakaba 1.77 !!!cp (106);
1918 wakaba 1.183 $self->{ca}->{value} .= chr ($self->{nc});
1919     $self->{read_until}->($self->{ca}->{value},
1920 wakaba 1.173 q['&],
1921 wakaba 1.183 length $self->{ca}->{value});
1922 wakaba 1.173
1923 wakaba 1.1 ## Stay in the state
1924     !!!next-input-character;
1925     redo A;
1926     }
1927 wakaba 1.57 } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1928 wakaba 1.187 if ($is_space->{$self->{nc}}) {
1929 wakaba 1.77 !!!cp (107);
1930 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1931 wakaba 1.1 !!!next-input-character;
1932     redo A;
1933 wakaba 1.183 } elsif ($self->{nc} == 0x0026) { # &
1934 wakaba 1.77 !!!cp (108);
1935 wakaba 1.167 ## NOTE: In the spec, the tokenizer is switched to the
1936     ## "entity in attribute value state". In this implementation, the
1937     ## tokenizer is switched to the |ENTITY_STATE|, which is an
1938     ## implementation of the "consume a character reference" algorithm.
1939 wakaba 1.183 $self->{entity_add} = -1;
1940 wakaba 1.169 $self->{prev_state} = $self->{state};
1941 wakaba 1.167 $self->{state} = ENTITY_STATE;
1942 wakaba 1.1 !!!next-input-character;
1943     redo A;
1944 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
1945     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1946 wakaba 1.77 !!!cp (109);
1947 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1948     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1949 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1950 wakaba 1.183 if ($self->{ct}->{attributes}) {
1951 wakaba 1.77 !!!cp (110);
1952 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1953 wakaba 1.77 } else {
1954 wakaba 1.78 ## NOTE: This state should never be reached.
1955 wakaba 1.77 !!!cp (111);
1956 wakaba 1.1 }
1957     } else {
1958 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1959 wakaba 1.1 }
1960 wakaba 1.57 $self->{state} = DATA_STATE;
1961 wakaba 1.1 !!!next-input-character;
1962    
1963 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1964 wakaba 1.1
1965     redo A;
1966 wakaba 1.183 } elsif ($self->{nc} == -1) {
1967 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1968 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1969 wakaba 1.77 !!!cp (112);
1970 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
1971     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1972 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1973 wakaba 1.183 if ($self->{ct}->{attributes}) {
1974 wakaba 1.77 !!!cp (113);
1975 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1976 wakaba 1.77 } else {
1977 wakaba 1.78 ## NOTE: This state should never be reached.
1978 wakaba 1.77 !!!cp (114);
1979 wakaba 1.1 }
1980     } else {
1981 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
1982 wakaba 1.1 }
1983 wakaba 1.57 $self->{state} = DATA_STATE;
1984 wakaba 1.1 ## reconsume
1985    
1986 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
1987 wakaba 1.1
1988     redo A;
1989     } else {
1990 wakaba 1.72 if ({
1991     0x0022 => 1, # "
1992     0x0027 => 1, # '
1993     0x003D => 1, # =
1994 wakaba 1.183 }->{$self->{nc}}) {
1995 wakaba 1.77 !!!cp (115);
1996 wakaba 1.72 !!!parse-error (type => 'bad attribute value');
1997 wakaba 1.77 } else {
1998     !!!cp (116);
1999 wakaba 1.72 }
2000 wakaba 1.183 $self->{ca}->{value} .= chr ($self->{nc});
2001     $self->{read_until}->($self->{ca}->{value},
2002 wakaba 1.173 q["'=& >],
2003 wakaba 1.183 length $self->{ca}->{value});
2004 wakaba 1.173
2005 wakaba 1.1 ## Stay in the state
2006     !!!next-input-character;
2007     redo A;
2008     }
2009 wakaba 1.72 } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2010 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2011 wakaba 1.77 !!!cp (118);
2012 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2013     !!!next-input-character;
2014     redo A;
2015 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2016     if ($self->{ct}->{type} == START_TAG_TOKEN) {
2017 wakaba 1.77 !!!cp (119);
2018 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
2019     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2020 wakaba 1.72 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2021 wakaba 1.183 if ($self->{ct}->{attributes}) {
2022 wakaba 1.77 !!!cp (120);
2023 wakaba 1.72 !!!parse-error (type => 'end tag attribute');
2024 wakaba 1.77 } else {
2025 wakaba 1.78 ## NOTE: This state should never be reached.
2026 wakaba 1.77 !!!cp (121);
2027 wakaba 1.72 }
2028     } else {
2029 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
2030 wakaba 1.72 }
2031     $self->{state} = DATA_STATE;
2032     !!!next-input-character;
2033    
2034 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
2035 wakaba 1.72
2036     redo A;
2037 wakaba 1.183 } elsif ($self->{nc} == 0x002F) { # /
2038 wakaba 1.125 !!!cp (122);
2039     $self->{state} = SELF_CLOSING_START_TAG_STATE;
2040 wakaba 1.72 !!!next-input-character;
2041 wakaba 1.125 redo A;
2042 wakaba 1.183 } elsif ($self->{nc} == -1) {
2043 wakaba 1.141 !!!parse-error (type => 'unclosed tag');
2044 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2045 wakaba 1.141 !!!cp (122.3);
2046 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
2047     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2048     if ($self->{ct}->{attributes}) {
2049 wakaba 1.141 !!!cp (122.1);
2050     !!!parse-error (type => 'end tag attribute');
2051     } else {
2052     ## NOTE: This state should never be reached.
2053     !!!cp (122.2);
2054     }
2055     } else {
2056 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
2057 wakaba 1.141 }
2058     $self->{state} = DATA_STATE;
2059     ## Reconsume.
2060 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
2061 wakaba 1.141 redo A;
2062 wakaba 1.125 } else {
2063     !!!cp ('124.1');
2064     !!!parse-error (type => 'no space between attributes');
2065     $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2066     ## reconsume
2067     redo A;
2068     }
2069     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2070 wakaba 1.183 if ($self->{nc} == 0x003E) { # >
2071     if ($self->{ct}->{type} == END_TAG_TOKEN) {
2072 wakaba 1.125 !!!cp ('124.2');
2073 wakaba 1.183 !!!parse-error (type => 'nestc', token => $self->{ct});
2074 wakaba 1.125 ## TODO: Different type than slash in start tag
2075     $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2076 wakaba 1.183 if ($self->{ct}->{attributes}) {
2077 wakaba 1.125 !!!cp ('124.4');
2078     !!!parse-error (type => 'end tag attribute');
2079     } else {
2080     !!!cp ('124.5');
2081     }
2082     ## TODO: Test |<title></title/>|
2083 wakaba 1.72 } else {
2084 wakaba 1.125 !!!cp ('124.3');
2085     $self->{self_closing} = 1;
2086 wakaba 1.72 }
2087 wakaba 1.125
2088     $self->{state} = DATA_STATE;
2089     !!!next-input-character;
2090    
2091 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
2092 wakaba 1.125
2093 wakaba 1.72 redo A;
2094 wakaba 1.183 } elsif ($self->{nc} == -1) {
2095 wakaba 1.141 !!!parse-error (type => 'unclosed tag');
2096 wakaba 1.183 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2097 wakaba 1.141 !!!cp (124.7);
2098 wakaba 1.183 $self->{last_stag_name} = $self->{ct}->{tag_name};
2099     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2100     if ($self->{ct}->{attributes}) {
2101 wakaba 1.141 !!!cp (124.5);
2102     !!!parse-error (type => 'end tag attribute');
2103     } else {
2104     ## NOTE: This state should never be reached.
2105     !!!cp (124.6);
2106     }
2107     } else {
2108 wakaba 1.183 die "$0: $self->{ct}->{type}: Unknown token type";
2109 wakaba 1.141 }
2110     $self->{state} = DATA_STATE;
2111     ## Reconsume.
2112 wakaba 1.183 !!!emit ($self->{ct}); # start tag or end tag
2113 wakaba 1.141 redo A;
2114 wakaba 1.72 } else {
2115 wakaba 1.125 !!!cp ('124.4');
2116     !!!parse-error (type => 'nestc');
2117     ## TODO: This error type is wrong.
2118 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2119 wakaba 1.125 ## Reconsume.
2120 wakaba 1.72 redo A;
2121     }
2122 wakaba 1.57 } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2123 wakaba 1.1 ## (only happen if PCDATA state)
2124 wakaba 1.167
2125     ## NOTE: Unlike spec's "bogus comment state", this implementation
2126     ## consumes characters one-by-one basis.
2127 wakaba 1.1
2128 wakaba 1.183 if ($self->{nc} == 0x003E) { # >
2129 wakaba 1.167 !!!cp (124);
2130     $self->{state} = DATA_STATE;
2131     !!!next-input-character;
2132 wakaba 1.1
2133 wakaba 1.183 !!!emit ($self->{ct}); # comment
2134 wakaba 1.167 redo A;
2135 wakaba 1.183 } elsif ($self->{nc} == -1) {
2136 wakaba 1.167 !!!cp (125);
2137     $self->{state} = DATA_STATE;
2138     ## reconsume
2139 wakaba 1.1
2140 wakaba 1.183 !!!emit ($self->{ct}); # comment
2141 wakaba 1.167 redo A;
2142     } else {
2143     !!!cp (126);
2144 wakaba 1.183 $self->{ct}->{data} .= chr ($self->{nc}); # comment
2145     $self->{read_until}->($self->{ct}->{data},
2146 wakaba 1.173 q[>],
2147 wakaba 1.183 length $self->{ct}->{data});
2148 wakaba 1.173
2149 wakaba 1.167 ## Stay in the state.
2150     !!!next-input-character;
2151     redo A;
2152     }
2153 wakaba 1.57 } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2154 wakaba 1.1 ## (only happen if PCDATA state)
2155    
2156 wakaba 1.183 if ($self->{nc} == 0x002D) { # -
2157 wakaba 1.163 !!!cp (133);
2158     $self->{state} = MD_HYPHEN_STATE;
2159 wakaba 1.1 !!!next-input-character;
2160 wakaba 1.163 redo A;
2161 wakaba 1.183 } elsif ($self->{nc} == 0x0044 or # D
2162     $self->{nc} == 0x0064) { # d
2163 wakaba 1.163 ## ASCII case-insensitive.
2164     !!!cp (130);
2165     $self->{state} = MD_DOCTYPE_STATE;
2166 wakaba 1.183 $self->{s_kwd} = chr $self->{nc};
2167 wakaba 1.1 !!!next-input-character;
2168 wakaba 1.163 redo A;
2169 wakaba 1.127 } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2170     $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2171 wakaba 1.183 $self->{nc} == 0x005B) { # [
2172 wakaba 1.163 !!!cp (135.4);
2173     $self->{state} = MD_CDATA_STATE;
2174 wakaba 1.183 $self->{s_kwd} = '[';
2175 wakaba 1.127 !!!next-input-character;
2176 wakaba 1.163 redo A;
2177 wakaba 1.77 } else {
2178     !!!cp (136);
2179 wakaba 1.1 }
2180    
2181 wakaba 1.163 !!!parse-error (type => 'bogus comment',
2182     line => $self->{line_prev},
2183     column => $self->{column_prev} - 1);
2184     ## Reconsume.
2185 wakaba 1.57 $self->{state} = BOGUS_COMMENT_STATE;
2186 wakaba 1.183 $self->{ct} = {type => COMMENT_TOKEN, data => '',
2187 wakaba 1.163 line => $self->{line_prev},
2188     column => $self->{column_prev} - 1,
2189 wakaba 1.118 };
2190 wakaba 1.1 redo A;
2191 wakaba 1.163 } elsif ($self->{state} == MD_HYPHEN_STATE) {
2192 wakaba 1.183 if ($self->{nc} == 0x002D) { # -
2193 wakaba 1.163 !!!cp (127);
2194 wakaba 1.183 $self->{ct} = {type => COMMENT_TOKEN, data => '',
2195 wakaba 1.163 line => $self->{line_prev},
2196     column => $self->{column_prev} - 2,
2197     };
2198     $self->{state} = COMMENT_START_STATE;
2199     !!!next-input-character;
2200     redo A;
2201     } else {
2202     !!!cp (128);
2203     !!!parse-error (type => 'bogus comment',
2204     line => $self->{line_prev},
2205     column => $self->{column_prev} - 2);
2206     $self->{state} = BOGUS_COMMENT_STATE;
2207     ## Reconsume.
2208 wakaba 1.183 $self->{ct} = {type => COMMENT_TOKEN,
2209 wakaba 1.163 data => '-',
2210     line => $self->{line_prev},
2211     column => $self->{column_prev} - 2,
2212     };
2213     redo A;
2214     }
2215     } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2216     ## ASCII case-insensitive.
2217 wakaba 1.183 if ($self->{nc} == [
2218 wakaba 1.163 undef,
2219     0x004F, # O
2220     0x0043, # C
2221     0x0054, # T
2222     0x0059, # Y
2223     0x0050, # P
2224 wakaba 1.183 ]->[length $self->{s_kwd}] or
2225     $self->{nc} == [
2226 wakaba 1.163 undef,
2227     0x006F, # o
2228     0x0063, # c
2229     0x0074, # t
2230     0x0079, # y
2231     0x0070, # p
2232 wakaba 1.183 ]->[length $self->{s_kwd}]) {
2233 wakaba 1.163 !!!cp (131);
2234     ## Stay in the state.
2235 wakaba 1.183 $self->{s_kwd} .= chr $self->{nc};
2236 wakaba 1.163 !!!next-input-character;
2237     redo A;
2238 wakaba 1.183 } elsif ((length $self->{s_kwd}) == 6 and
2239     ($self->{nc} == 0x0045 or # E
2240     $self->{nc} == 0x0065)) { # e
2241 wakaba 1.163 !!!cp (129);
2242     $self->{state} = DOCTYPE_STATE;
2243 wakaba 1.183 $self->{ct} = {type => DOCTYPE_TOKEN,
2244 wakaba 1.163 quirks => 1,
2245     line => $self->{line_prev},
2246     column => $self->{column_prev} - 7,
2247     };
2248     !!!next-input-character;
2249     redo A;
2250     } else {
2251     !!!cp (132);
2252     !!!parse-error (type => 'bogus comment',
2253     line => $self->{line_prev},
2254 wakaba 1.183 column => $self->{column_prev} - 1 - length $self->{s_kwd});
2255 wakaba 1.163 $self->{state} = BOGUS_COMMENT_STATE;
2256     ## Reconsume.
2257 wakaba 1.183 $self->{ct} = {type => COMMENT_TOKEN,
2258     data => $self->{s_kwd},
2259 wakaba 1.163 line => $self->{line_prev},
2260 wakaba 1.183 column => $self->{column_prev} - 1 - length $self->{s_kwd},
2261 wakaba 1.163 };
2262     redo A;
2263     }
2264     } elsif ($self->{state} == MD_CDATA_STATE) {
2265 wakaba 1.183 if ($self->{nc} == {
2266 wakaba 1.163 '[' => 0x0043, # C
2267     '[C' => 0x0044, # D
2268     '[CD' => 0x0041, # A
2269     '[CDA' => 0x0054, # T
2270     '[CDAT' => 0x0041, # A
2271 wakaba 1.183 }->{$self->{s_kwd}}) {
2272 wakaba 1.163 !!!cp (135.1);
2273     ## Stay in the state.
2274 wakaba 1.183 $self->{s_kwd} .= chr $self->{nc};
2275 wakaba 1.163 !!!next-input-character;
2276     redo A;
2277 wakaba 1.183 } elsif ($self->{s_kwd} eq '[CDATA' and
2278     $self->{nc} == 0x005B) { # [
2279 wakaba 1.163 !!!cp (135.2);
2280 wakaba 1.183 $self->{ct} = {type => CHARACTER_TOKEN,
2281 wakaba 1.165 data => '',
2282     line => $self->{line_prev},
2283     column => $self->{column_prev} - 7};
2284     $self->{state} = CDATA_SECTION_STATE;
2285 wakaba 1.163 !!!next-input-character;
2286     redo A;
2287     } else {
2288     !!!cp (135.3);
2289     !!!parse-error (type => 'bogus comment',
2290     line => $self->{line_prev},
2291 wakaba 1.183 column => $self->{column_prev} - 1 - length $self->{s_kwd});
2292 wakaba 1.163 $self->{state} = BOGUS_COMMENT_STATE;
2293     ## Reconsume.
2294 wakaba 1.183 $self->{ct} = {type => COMMENT_TOKEN,
2295     data => $self->{s_kwd},
2296 wakaba 1.163 line => $self->{line_prev},
2297 wakaba 1.183 column => $self->{column_prev} - 1 - length $self->{s_kwd},
2298 wakaba 1.163 };
2299     redo A;
2300     }
2301 wakaba 1.57 } elsif ($self->{state} == COMMENT_START_STATE) {
2302 wakaba 1.183 if ($self->{nc} == 0x002D) { # -
2303 wakaba 1.77 !!!cp (137);
2304 wakaba 1.57 $self->{state} = COMMENT_START_DASH_STATE;
2305 wakaba 1.23 !!!next-input-character;
2306     redo A;
2307 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2308 wakaba 1.77 !!!cp (138);
2309 wakaba 1.23 !!!parse-error (type => 'bogus comment');
2310 wakaba 1.57 $self->{state} = DATA_STATE;
2311 wakaba 1.23 !!!next-input-character;
2312    
2313 wakaba 1.183 !!!emit ($self->{ct}); # comment
2314 wakaba 1.23
2315     redo A;
2316 wakaba 1.183 } elsif ($self->{nc} == -1) {
2317 wakaba 1.77 !!!cp (139);
2318 wakaba 1.23 !!!parse-error (type => 'unclosed comment');
2319 wakaba 1.57 $self->{state} = DATA_STATE;
2320 wakaba 1.23 ## reconsume
2321    
2322 wakaba 1.183 !!!emit ($self->{ct}); # comment
2323 wakaba 1.23
2324     redo A;
2325     } else {
2326 wakaba 1.77 !!!cp (140);
2327 wakaba 1.183 $self->{ct}->{data} # comment
2328     .= chr ($self->{nc});
2329 wakaba 1.57 $self->{state} = COMMENT_STATE;
2330 wakaba 1.23 !!!next-input-character;
2331     redo A;
2332     }
2333 wakaba 1.57 } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2334 wakaba 1.183 if ($self->{nc} == 0x002D) { # -
2335 wakaba 1.77 !!!cp (141);
2336 wakaba 1.57 $self->{state} = COMMENT_END_STATE;
2337 wakaba 1.23 !!!next-input-character;
2338     redo A;
2339 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2340 wakaba 1.77 !!!cp (142);
2341 wakaba 1.23 !!!parse-error (type => 'bogus comment');
2342 wakaba 1.57 $self->{state} = DATA_STATE;
2343 wakaba 1.23 !!!next-input-character;
2344    
2345 wakaba 1.183 !!!emit ($self->{ct}); # comment
2346 wakaba 1.23
2347     redo A;
2348 wakaba 1.183 } elsif ($self->{nc} == -1) {
2349 wakaba 1.77 !!!cp (143);
2350 wakaba 1.23 !!!parse-error (type => 'unclosed comment');
2351 wakaba 1.57 $self->{state} = DATA_STATE;
2352 wakaba 1.23 ## reconsume
2353    
2354 wakaba 1.183 !!!emit ($self->{ct}); # comment
2355 wakaba 1.23
2356     redo A;
2357     } else {
2358 wakaba 1.77 !!!cp (144);
2359 wakaba 1.183 $self->{ct}->{data} # comment
2360     .= '-' . chr ($self->{nc});
2361 wakaba 1.57 $self->{state} = COMMENT_STATE;
2362 wakaba 1.23 !!!next-input-character;
2363     redo A;
2364     }
2365 wakaba 1.57 } elsif ($self->{state} == COMMENT_STATE) {
2366 wakaba 1.183 if ($self->{nc} == 0x002D) { # -
2367 wakaba 1.77 !!!cp (145);
2368 wakaba 1.57 $self->{state} = COMMENT_END_DASH_STATE;
2369 wakaba 1.1 !!!next-input-character;
2370     redo A;
2371 wakaba 1.183 } elsif ($self->{nc} == -1) {
2372 wakaba 1.77 !!!cp (146);
2373 wakaba 1.3 !!!parse-error (type => 'unclosed comment');
2374 wakaba 1.57 $self->{state} = DATA_STATE;
2375 wakaba 1.1 ## reconsume
2376    
2377 wakaba 1.183 !!!emit ($self->{ct}); # comment
2378 wakaba 1.1
2379     redo A;
2380     } else {
2381 wakaba 1.77 !!!cp (147);
2382 wakaba 1.183 $self->{ct}->{data} .= chr ($self->{nc}); # comment
2383     $self->{read_until}->($self->{ct}->{data},
2384 wakaba 1.173 q[-],
2385 wakaba 1.183 length $self->{ct}->{data});
2386 wakaba 1.173
2387 wakaba 1.1 ## Stay in the state
2388     !!!next-input-character;
2389     redo A;
2390     }
2391 wakaba 1.57 } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2392 wakaba 1.183 if ($self->{nc} == 0x002D) { # -
2393 wakaba 1.77 !!!cp (148);
2394 wakaba 1.57 $self->{state} = COMMENT_END_STATE;
2395 wakaba 1.1 !!!next-input-character;
2396     redo A;
2397 wakaba 1.183 } elsif ($self->{nc} == -1) {
2398 wakaba 1.77 !!!cp (149);
2399 wakaba 1.3 !!!parse-error (type => 'unclosed comment');
2400 wakaba 1.57 $self->{state} = DATA_STATE;
2401 wakaba 1.1 ## reconsume
2402    
2403 wakaba 1.183 !!!emit ($self->{ct}); # comment
2404 wakaba 1.1
2405     redo A;
2406     } else {
2407 wakaba 1.77 !!!cp (150);
2408 wakaba 1.183 $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2409 wakaba 1.57 $self->{state} = COMMENT_STATE;
2410 wakaba 1.1 !!!next-input-character;
2411     redo A;
2412     }
2413 wakaba 1.57 } elsif ($self->{state} == COMMENT_END_STATE) {
2414 wakaba 1.183 if ($self->{nc} == 0x003E) { # >
2415 wakaba 1.77 !!!cp (151);
2416 wakaba 1.57 $self->{state} = DATA_STATE;
2417 wakaba 1.1 !!!next-input-character;
2418    
2419 wakaba 1.183 !!!emit ($self->{ct}); # comment
2420 wakaba 1.1
2421     redo A;
2422 wakaba 1.183 } elsif ($self->{nc} == 0x002D) { # -
2423 wakaba 1.77 !!!cp (152);
2424 wakaba 1.114 !!!parse-error (type => 'dash in comment',
2425     line => $self->{line_prev},
2426     column => $self->{column_prev});
2427 wakaba 1.183 $self->{ct}->{data} .= '-'; # comment
2428 wakaba 1.1 ## Stay in the state
2429     !!!next-input-character;
2430     redo A;
2431 wakaba 1.183 } elsif ($self->{nc} == -1) {
2432 wakaba 1.77 !!!cp (153);
2433 wakaba 1.3 !!!parse-error (type => 'unclosed comment');
2434 wakaba 1.57 $self->{state} = DATA_STATE;
2435 wakaba 1.1 ## reconsume
2436    
2437 wakaba 1.183 !!!emit ($self->{ct}); # comment
2438 wakaba 1.1
2439     redo A;
2440     } else {
2441 wakaba 1.77 !!!cp (154);
2442 wakaba 1.114 !!!parse-error (type => 'dash in comment',
2443     line => $self->{line_prev},
2444     column => $self->{column_prev});
2445 wakaba 1.183 $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2446 wakaba 1.57 $self->{state} = COMMENT_STATE;
2447 wakaba 1.1 !!!next-input-character;
2448     redo A;
2449     }
2450 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_STATE) {
2451 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2452 wakaba 1.77 !!!cp (155);
2453 wakaba 1.57 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2454 wakaba 1.1 !!!next-input-character;
2455     redo A;
2456     } else {
2457 wakaba 1.77 !!!cp (156);
2458 wakaba 1.3 !!!parse-error (type => 'no space before DOCTYPE name');
2459 wakaba 1.57 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2460 wakaba 1.1 ## reconsume
2461     redo A;
2462     }
2463 wakaba 1.57 } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2464 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2465 wakaba 1.77 !!!cp (157);
2466 wakaba 1.1 ## Stay in the state
2467     !!!next-input-character;
2468     redo A;
2469 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2470 wakaba 1.77 !!!cp (158);
2471 wakaba 1.3 !!!parse-error (type => 'no DOCTYPE name');
2472 wakaba 1.57 $self->{state} = DATA_STATE;
2473 wakaba 1.1 !!!next-input-character;
2474    
2475 wakaba 1.183 !!!emit ($self->{ct}); # DOCTYPE (quirks)
2476 wakaba 1.1
2477     redo A;
2478 wakaba 1.183 } elsif ($self->{nc} == -1) {
2479 wakaba 1.77 !!!cp (159);
2480 wakaba 1.3 !!!parse-error (type => 'no DOCTYPE name');
2481 wakaba 1.57 $self->{state} = DATA_STATE;
2482 wakaba 1.1 ## reconsume
2483    
2484 wakaba 1.183 !!!emit ($self->{ct}); # DOCTYPE (quirks)
2485 wakaba 1.1
2486     redo A;
2487     } else {
2488 wakaba 1.77 !!!cp (160);
2489 wakaba 1.183 $self->{ct}->{name} = chr $self->{nc};
2490     delete $self->{ct}->{quirks};
2491 wakaba 1.57 $self->{state} = DOCTYPE_NAME_STATE;
2492 wakaba 1.1 !!!next-input-character;
2493     redo A;
2494     }
2495 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2496 wakaba 1.18 ## ISSUE: Redundant "First," in the spec.
2497 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2498 wakaba 1.77 !!!cp (161);
2499 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2500 wakaba 1.1 !!!next-input-character;
2501     redo A;
2502 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2503 wakaba 1.77 !!!cp (162);
2504 wakaba 1.57 $self->{state} = DATA_STATE;
2505 wakaba 1.1 !!!next-input-character;
2506    
2507 wakaba 1.183 !!!emit ($self->{ct}); # DOCTYPE
2508 wakaba 1.1
2509     redo A;
2510 wakaba 1.183 } elsif ($self->{nc} == -1) {
2511 wakaba 1.77 !!!cp (163);
2512 wakaba 1.3 !!!parse-error (type => 'unclosed DOCTYPE');
2513 wakaba 1.57 $self->{state} = DATA_STATE;
2514 wakaba 1.1 ## reconsume
2515    
2516 wakaba 1.183 $self->{ct}->{quirks} = 1;
2517     !!!emit ($self->{ct}); # DOCTYPE
2518 wakaba 1.1
2519     redo A;
2520     } else {
2521 wakaba 1.77 !!!cp (164);
2522 wakaba 1.183 $self->{ct}->{name}
2523     .= chr ($self->{nc}); # DOCTYPE
2524 wakaba 1.1 ## Stay in the state
2525     !!!next-input-character;
2526     redo A;
2527     }
2528 wakaba 1.57 } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2529 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2530 wakaba 1.77 !!!cp (165);
2531 wakaba 1.1 ## Stay in the state
2532     !!!next-input-character;
2533     redo A;
2534 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2535 wakaba 1.77 !!!cp (166);
2536 wakaba 1.57 $self->{state} = DATA_STATE;
2537 wakaba 1.1 !!!next-input-character;
2538    
2539 wakaba 1.183 !!!emit ($self->{ct}); # DOCTYPE
2540 wakaba 1.1
2541     redo A;
2542 wakaba 1.183 } elsif ($self->{nc} == -1) {
2543 wakaba 1.77 !!!cp (167);
2544 wakaba 1.3 !!!parse-error (type => 'unclosed DOCTYPE');
2545 wakaba 1.57 $self->{state} = DATA_STATE;
2546 wakaba 1.1 ## reconsume
2547    
2548 wakaba 1.183 $self->{ct}->{quirks} = 1;
2549     !!!emit ($self->{ct}); # DOCTYPE
2550 wakaba 1.18
2551     redo A;
2552 wakaba 1.183 } elsif ($self->{nc} == 0x0050 or # P
2553     $self->{nc} == 0x0070) { # p
2554 wakaba 1.166 $self->{state} = PUBLIC_STATE;
2555 wakaba 1.183 $self->{s_kwd} = chr $self->{nc};
2556 wakaba 1.18 !!!next-input-character;
2557 wakaba 1.166 redo A;
2558 wakaba 1.183 } elsif ($self->{nc} == 0x0053 or # S
2559     $self->{nc} == 0x0073) { # s
2560 wakaba 1.166 $self->{state} = SYSTEM_STATE;
2561 wakaba 1.183 $self->{s_kwd} = chr $self->{nc};
2562 wakaba 1.18 !!!next-input-character;
2563 wakaba 1.166 redo A;
2564 wakaba 1.18 } else {
2565 wakaba 1.77 !!!cp (180);
2566 wakaba 1.166 !!!parse-error (type => 'string after DOCTYPE name');
2567 wakaba 1.183 $self->{ct}->{quirks} = 1;
2568 wakaba 1.166
2569     $self->{state} = BOGUS_DOCTYPE_STATE;
2570 wakaba 1.18 !!!next-input-character;
2571 wakaba 1.166 redo A;
2572 wakaba 1.18 }
2573 wakaba 1.166 } elsif ($self->{state} == PUBLIC_STATE) {
2574     ## ASCII case-insensitive
2575 wakaba 1.183 if ($self->{nc} == [
2576 wakaba 1.166 undef,
2577     0x0055, # U
2578     0x0042, # B
2579     0x004C, # L
2580     0x0049, # I
2581 wakaba 1.183 ]->[length $self->{s_kwd}] or
2582     $self->{nc} == [
2583 wakaba 1.166 undef,
2584     0x0075, # u
2585     0x0062, # b
2586     0x006C, # l
2587     0x0069, # i
2588 wakaba 1.183 ]->[length $self->{s_kwd}]) {
2589 wakaba 1.166 !!!cp (175);
2590     ## Stay in the state.
2591 wakaba 1.183 $self->{s_kwd} .= chr $self->{nc};
2592 wakaba 1.166 !!!next-input-character;
2593     redo A;
2594 wakaba 1.183 } elsif ((length $self->{s_kwd}) == 5 and
2595     ($self->{nc} == 0x0043 or # C
2596     $self->{nc} == 0x0063)) { # c
2597 wakaba 1.166 !!!cp (168);
2598     $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2599     !!!next-input-character;
2600     redo A;
2601     } else {
2602     !!!cp (169);
2603     !!!parse-error (type => 'string after DOCTYPE name',
2604     line => $self->{line_prev},
2605 wakaba 1.183 column => $self->{column_prev} + 1 - length $self->{s_kwd});
2606     $self->{ct}->{quirks} = 1;
2607 wakaba 1.18
2608 wakaba 1.166 $self->{state} = BOGUS_DOCTYPE_STATE;
2609     ## Reconsume.
2610     redo A;
2611     }
2612     } elsif ($self->{state} == SYSTEM_STATE) {
2613     ## ASCII case-insensitive
2614 wakaba 1.183 if ($self->{nc} == [
2615 wakaba 1.166 undef,
2616     0x0059, # Y
2617     0x0053, # S
2618     0x0054, # T
2619     0x0045, # E
2620 wakaba 1.183 ]->[length $self->{s_kwd}] or
2621     $self->{nc} == [
2622 wakaba 1.166 undef,
2623     0x0079, # y
2624     0x0073, # s
2625     0x0074, # t
2626     0x0065, # e
2627 wakaba 1.183 ]->[length $self->{s_kwd}]) {
2628 wakaba 1.166 !!!cp (170);
2629     ## Stay in the state.
2630 wakaba 1.183 $self->{s_kwd} .= chr $self->{nc};
2631 wakaba 1.166 !!!next-input-character;
2632     redo A;
2633 wakaba 1.183 } elsif ((length $self->{s_kwd}) == 5 and
2634     ($self->{nc} == 0x004D or # M
2635     $self->{nc} == 0x006D)) { # m
2636 wakaba 1.166 !!!cp (171);
2637     $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2638     !!!next-input-character;
2639     redo A;
2640     } else {
2641     !!!cp (172);
2642     !!!parse-error (type => 'string after DOCTYPE name',
2643     line => $self->{line_prev},
2644 wakaba 1.183 column => $self->{column_prev} + 1 - length $self->{s_kwd});
2645     $self->{ct}->{quirks} = 1;
2646 wakaba 1.73
2647 wakaba 1.166 $self->{state} = BOGUS_DOCTYPE_STATE;
2648     ## Reconsume.
2649     redo A;
2650     }
2651 wakaba 1.57 } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2652 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2653 wakaba 1.77 !!!cp (181);
2654 wakaba 1.18 ## Stay in the state
2655     !!!next-input-character;
2656     redo A;
2657 wakaba 1.183 } elsif ($self->{nc} eq 0x0022) { # "
2658 wakaba 1.77 !!!cp (182);
2659 wakaba 1.183 $self->{ct}->{pubid} = ''; # DOCTYPE
2660 wakaba 1.57 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2661 wakaba 1.18 !!!next-input-character;
2662     redo A;
2663 wakaba 1.183 } elsif ($self->{nc} eq 0x0027) { # '
2664 wakaba 1.77 !!!cp (183);
2665 wakaba 1.183 $self->{ct}->{pubid} = ''; # DOCTYPE
2666 wakaba 1.57 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2667 wakaba 1.18 !!!next-input-character;
2668     redo A;
2669 wakaba 1.183 } elsif ($self->{nc} eq 0x003E) { # >
2670 wakaba 1.77 !!!cp (184);
2671 wakaba 1.18 !!!parse-error (type => 'no PUBLIC literal');
2672    
2673 wakaba 1.57 $self->{state} = DATA_STATE;
2674 wakaba 1.18 !!!next-input-character;
2675    
2676 wakaba 1.183 $self->{ct}->{quirks} = 1;
2677     !!!emit ($self->{ct}); # DOCTYPE
2678 wakaba 1.18
2679     redo A;
2680 wakaba 1.183 } elsif ($self->{nc} == -1) {
2681 wakaba 1.77 !!!cp (185);
2682 wakaba 1.18 !!!parse-error (type => 'unclosed DOCTYPE');
2683    
2684 wakaba 1.57 $self->{state} = DATA_STATE;
2685 wakaba 1.18 ## reconsume
2686    
2687 wakaba 1.183 $self->{ct}->{quirks} = 1;
2688     !!!emit ($self->{ct}); # DOCTYPE
2689 wakaba 1.18
2690     redo A;
2691     } else {
2692 wakaba 1.77 !!!cp (186);
2693 wakaba 1.18 !!!parse-error (type => 'string after PUBLIC');
2694 wakaba 1.183 $self->{ct}->{quirks} = 1;
2695 wakaba 1.73
2696 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
2697 wakaba 1.18 !!!next-input-character;
2698     redo A;
2699     }
2700 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2701 wakaba 1.183 if ($self->{nc} == 0x0022) { # "
2702 wakaba 1.77 !!!cp (187);
2703 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2704 wakaba 1.18 !!!next-input-character;
2705     redo A;
2706 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2707 wakaba 1.77 !!!cp (188);
2708 wakaba 1.69 !!!parse-error (type => 'unclosed PUBLIC literal');
2709    
2710     $self->{state} = DATA_STATE;
2711     !!!next-input-character;
2712    
2713 wakaba 1.183 $self->{ct}->{quirks} = 1;
2714     !!!emit ($self->{ct}); # DOCTYPE
2715 wakaba 1.69
2716     redo A;
2717 wakaba 1.183 } elsif ($self->{nc} == -1) {
2718 wakaba 1.77 !!!cp (189);
2719 wakaba 1.18 !!!parse-error (type => 'unclosed PUBLIC literal');
2720    
2721 wakaba 1.57 $self->{state} = DATA_STATE;
2722 wakaba 1.18 ## reconsume
2723    
2724 wakaba 1.183 $self->{ct}->{quirks} = 1;
2725     !!!emit ($self->{ct}); # DOCTYPE
2726 wakaba 1.18
2727     redo A;
2728     } else {
2729 wakaba 1.77 !!!cp (190);
2730 wakaba 1.183 $self->{ct}->{pubid} # DOCTYPE
2731     .= chr $self->{nc};
2732     $self->{read_until}->($self->{ct}->{pubid}, q[">],
2733     length $self->{ct}->{pubid});
2734 wakaba 1.173
2735 wakaba 1.18 ## Stay in the state
2736     !!!next-input-character;
2737     redo A;
2738     }
2739 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2740 wakaba 1.183 if ($self->{nc} == 0x0027) { # '
2741 wakaba 1.77 !!!cp (191);
2742 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2743 wakaba 1.18 !!!next-input-character;
2744     redo A;
2745 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2746 wakaba 1.77 !!!cp (192);
2747 wakaba 1.69 !!!parse-error (type => 'unclosed PUBLIC literal');
2748    
2749     $self->{state} = DATA_STATE;
2750     !!!next-input-character;
2751    
2752 wakaba 1.183 $self->{ct}->{quirks} = 1;
2753     !!!emit ($self->{ct}); # DOCTYPE
2754 wakaba 1.69
2755     redo A;
2756 wakaba 1.183 } elsif ($self->{nc} == -1) {
2757 wakaba 1.77 !!!cp (193);
2758 wakaba 1.18 !!!parse-error (type => 'unclosed PUBLIC literal');
2759    
2760 wakaba 1.57 $self->{state} = DATA_STATE;
2761 wakaba 1.18 ## reconsume
2762    
2763 wakaba 1.183 $self->{ct}->{quirks} = 1;
2764     !!!emit ($self->{ct}); # DOCTYPE
2765 wakaba 1.18
2766     redo A;
2767     } else {
2768 wakaba 1.77 !!!cp (194);
2769 wakaba 1.183 $self->{ct}->{pubid} # DOCTYPE
2770     .= chr $self->{nc};
2771     $self->{read_until}->($self->{ct}->{pubid}, q['>],
2772     length $self->{ct}->{pubid});
2773 wakaba 1.173
2774 wakaba 1.18 ## Stay in the state
2775     !!!next-input-character;
2776     redo A;
2777     }
2778 wakaba 1.57 } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2779 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2780 wakaba 1.77 !!!cp (195);
2781 wakaba 1.18 ## Stay in the state
2782     !!!next-input-character;
2783     redo A;
2784 wakaba 1.183 } elsif ($self->{nc} == 0x0022) { # "
2785 wakaba 1.77 !!!cp (196);
2786 wakaba 1.183 $self->{ct}->{sysid} = ''; # DOCTYPE
2787 wakaba 1.57 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2788 wakaba 1.18 !!!next-input-character;
2789     redo A;
2790 wakaba 1.183 } elsif ($self->{nc} == 0x0027) { # '
2791 wakaba 1.77 !!!cp (197);
2792 wakaba 1.183 $self->{ct}->{sysid} = ''; # DOCTYPE
2793 wakaba 1.57 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2794 wakaba 1.18 !!!next-input-character;
2795     redo A;
2796 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2797 wakaba 1.77 !!!cp (198);
2798 wakaba 1.57 $self->{state} = DATA_STATE;
2799 wakaba 1.18 !!!next-input-character;
2800    
2801 wakaba 1.183 !!!emit ($self->{ct}); # DOCTYPE
2802 wakaba 1.18
2803     redo A;
2804 wakaba 1.183 } elsif ($self->{nc} == -1) {
2805 wakaba 1.77 !!!cp (199);
2806 wakaba 1.18 !!!parse-error (type => 'unclosed DOCTYPE');
2807    
2808 wakaba 1.57 $self->{state} = DATA_STATE;
2809 wakaba 1.26 ## reconsume
2810 wakaba 1.18
2811 wakaba 1.183 $self->{ct}->{quirks} = 1;
2812     !!!emit ($self->{ct}); # DOCTYPE
2813 wakaba 1.18
2814     redo A;
2815     } else {
2816 wakaba 1.77 !!!cp (200);
2817 wakaba 1.18 !!!parse-error (type => 'string after PUBLIC literal');
2818 wakaba 1.183 $self->{ct}->{quirks} = 1;
2819 wakaba 1.73
2820 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
2821 wakaba 1.18 !!!next-input-character;
2822     redo A;
2823     }
2824 wakaba 1.57 } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2825 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2826 wakaba 1.77 !!!cp (201);
2827 wakaba 1.18 ## Stay in the state
2828     !!!next-input-character;
2829     redo A;
2830 wakaba 1.183 } elsif ($self->{nc} == 0x0022) { # "
2831 wakaba 1.77 !!!cp (202);
2832 wakaba 1.183 $self->{ct}->{sysid} = ''; # DOCTYPE
2833 wakaba 1.57 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2834 wakaba 1.18 !!!next-input-character;
2835     redo A;
2836 wakaba 1.183 } elsif ($self->{nc} == 0x0027) { # '
2837 wakaba 1.77 !!!cp (203);
2838 wakaba 1.183 $self->{ct}->{sysid} = ''; # DOCTYPE
2839 wakaba 1.57 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2840 wakaba 1.18 !!!next-input-character;
2841     redo A;
2842 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2843 wakaba 1.77 !!!cp (204);
2844 wakaba 1.18 !!!parse-error (type => 'no SYSTEM literal');
2845 wakaba 1.57 $self->{state} = DATA_STATE;
2846 wakaba 1.18 !!!next-input-character;
2847    
2848 wakaba 1.183 $self->{ct}->{quirks} = 1;
2849     !!!emit ($self->{ct}); # DOCTYPE
2850 wakaba 1.18
2851     redo A;
2852 wakaba 1.183 } elsif ($self->{nc} == -1) {
2853 wakaba 1.77 !!!cp (205);
2854 wakaba 1.18 !!!parse-error (type => 'unclosed DOCTYPE');
2855    
2856 wakaba 1.57 $self->{state} = DATA_STATE;
2857 wakaba 1.26 ## reconsume
2858 wakaba 1.18
2859 wakaba 1.183 $self->{ct}->{quirks} = 1;
2860     !!!emit ($self->{ct}); # DOCTYPE
2861 wakaba 1.18
2862     redo A;
2863     } else {
2864 wakaba 1.77 !!!cp (206);
2865 wakaba 1.30 !!!parse-error (type => 'string after SYSTEM');
2866 wakaba 1.183 $self->{ct}->{quirks} = 1;
2867 wakaba 1.73
2868 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
2869 wakaba 1.18 !!!next-input-character;
2870     redo A;
2871     }
2872 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2873 wakaba 1.183 if ($self->{nc} == 0x0022) { # "
2874 wakaba 1.77 !!!cp (207);
2875 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2876 wakaba 1.18 !!!next-input-character;
2877     redo A;
2878 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2879 wakaba 1.77 !!!cp (208);
2880 wakaba 1.153 !!!parse-error (type => 'unclosed SYSTEM literal');
2881 wakaba 1.69
2882     $self->{state} = DATA_STATE;
2883     !!!next-input-character;
2884    
2885 wakaba 1.183 $self->{ct}->{quirks} = 1;
2886     !!!emit ($self->{ct}); # DOCTYPE
2887 wakaba 1.69
2888     redo A;
2889 wakaba 1.183 } elsif ($self->{nc} == -1) {
2890 wakaba 1.77 !!!cp (209);
2891 wakaba 1.18 !!!parse-error (type => 'unclosed SYSTEM literal');
2892    
2893 wakaba 1.57 $self->{state} = DATA_STATE;
2894 wakaba 1.18 ## reconsume
2895    
2896 wakaba 1.183 $self->{ct}->{quirks} = 1;
2897     !!!emit ($self->{ct}); # DOCTYPE
2898 wakaba 1.18
2899     redo A;
2900     } else {
2901 wakaba 1.77 !!!cp (210);
2902 wakaba 1.183 $self->{ct}->{sysid} # DOCTYPE
2903     .= chr $self->{nc};
2904     $self->{read_until}->($self->{ct}->{sysid}, q[">],
2905     length $self->{ct}->{sysid});
2906 wakaba 1.173
2907 wakaba 1.18 ## Stay in the state
2908     !!!next-input-character;
2909     redo A;
2910     }
2911 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2912 wakaba 1.183 if ($self->{nc} == 0x0027) { # '
2913 wakaba 1.77 !!!cp (211);
2914 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2915 wakaba 1.18 !!!next-input-character;
2916     redo A;
2917 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2918 wakaba 1.77 !!!cp (212);
2919 wakaba 1.153 !!!parse-error (type => 'unclosed SYSTEM literal');
2920 wakaba 1.69
2921     $self->{state} = DATA_STATE;
2922     !!!next-input-character;
2923    
2924 wakaba 1.183 $self->{ct}->{quirks} = 1;
2925     !!!emit ($self->{ct}); # DOCTYPE
2926 wakaba 1.69
2927     redo A;
2928 wakaba 1.183 } elsif ($self->{nc} == -1) {
2929 wakaba 1.77 !!!cp (213);
2930 wakaba 1.18 !!!parse-error (type => 'unclosed SYSTEM literal');
2931    
2932 wakaba 1.57 $self->{state} = DATA_STATE;
2933 wakaba 1.18 ## reconsume
2934    
2935 wakaba 1.183 $self->{ct}->{quirks} = 1;
2936     !!!emit ($self->{ct}); # DOCTYPE
2937 wakaba 1.1
2938     redo A;
2939     } else {
2940 wakaba 1.77 !!!cp (214);
2941 wakaba 1.183 $self->{ct}->{sysid} # DOCTYPE
2942     .= chr $self->{nc};
2943     $self->{read_until}->($self->{ct}->{sysid}, q['>],
2944     length $self->{ct}->{sysid});
2945 wakaba 1.173
2946 wakaba 1.18 ## Stay in the state
2947     !!!next-input-character;
2948     redo A;
2949     }
2950 wakaba 1.57 } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2951 wakaba 1.187 if ($is_space->{$self->{nc}}) {
2952 wakaba 1.77 !!!cp (215);
2953 wakaba 1.18 ## Stay in the state
2954     !!!next-input-character;
2955     redo A;
2956 wakaba 1.183 } elsif ($self->{nc} == 0x003E) { # >
2957 wakaba 1.77 !!!cp (216);
2958 wakaba 1.57 $self->{state} = DATA_STATE;
2959 wakaba 1.18 !!!next-input-character;
2960    
2961 wakaba 1.183 !!!emit ($self->{ct}); # DOCTYPE
2962 wakaba 1.18
2963     redo A;
2964 wakaba 1.183 } elsif ($self->{nc} == -1) {
2965 wakaba 1.77 !!!cp (217);
2966 wakaba 1.150 !!!parse-error (type => 'unclosed DOCTYPE');
2967 wakaba 1.57 $self->{state} = DATA_STATE;
2968 wakaba 1.26 ## reconsume
2969 wakaba 1.18
2970 wakaba 1.183 $self->{ct}->{quirks} = 1;
2971     !!!emit ($self->{ct}); # DOCTYPE
2972 wakaba 1.18
2973     redo A;
2974     } else {
2975 wakaba 1.77 !!!cp (218);
2976 wakaba 1.18 !!!parse-error (type => 'string after SYSTEM literal');
2977 wakaba 1.183 #$self->{ct}->{quirks} = 1;
2978 wakaba 1.73
2979 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
2980 wakaba 1.1 !!!next-input-character;
2981     redo A;
2982     }
2983 wakaba 1.57 } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2984 wakaba 1.183 if ($self->{nc} == 0x003E) { # >
2985 wakaba 1.77 !!!cp (219);
2986 wakaba 1.57 $self->{state} = DATA_STATE;
2987 wakaba 1.1 !!!next-input-character;
2988    
2989 wakaba 1.183 !!!emit ($self->{ct}); # DOCTYPE
2990 wakaba 1.1
2991     redo A;
2992 wakaba 1.183 } elsif ($self->{nc} == -1) {
2993 wakaba 1.77 !!!cp (220);
2994 wakaba 1.57 $self->{state} = DATA_STATE;
2995 wakaba 1.1 ## reconsume
2996    
2997 wakaba 1.183 !!!emit ($self->{ct}); # DOCTYPE
2998 wakaba 1.1
2999     redo A;
3000     } else {
3001 wakaba 1.77 !!!cp (221);
3002 wakaba 1.173 my $s = '';
3003     $self->{read_until}->($s, q[>], 0);
3004    
3005 wakaba 1.1 ## Stay in the state
3006     !!!next-input-character;
3007     redo A;
3008     }
3009 wakaba 1.165 } elsif ($self->{state} == CDATA_SECTION_STATE) {
3010     ## NOTE: "CDATA section state" in the state is jointly implemented
3011     ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3012     ## and |CDATA_SECTION_MSE2_STATE|.
3013 wakaba 1.127
3014 wakaba 1.183 if ($self->{nc} == 0x005D) { # ]
3015 wakaba 1.165 !!!cp (221.1);
3016     $self->{state} = CDATA_SECTION_MSE1_STATE;
3017     !!!next-input-character;
3018     redo A;
3019 wakaba 1.183 } elsif ($self->{nc} == -1) {
3020 wakaba 1.165 $self->{state} = DATA_STATE;
3021     !!!next-input-character;
3022 wakaba 1.183 if (length $self->{ct}->{data}) { # character
3023 wakaba 1.165 !!!cp (221.2);
3024 wakaba 1.183 !!!emit ($self->{ct}); # character
3025 wakaba 1.165 } else {
3026     !!!cp (221.3);
3027 wakaba 1.183 ## No token to emit. $self->{ct} is discarded.
3028 wakaba 1.165 }
3029     redo A;
3030     } else {
3031     !!!cp (221.4);
3032 wakaba 1.183 $self->{ct}->{data} .= chr $self->{nc};
3033     $self->{read_until}->($self->{ct}->{data},
3034 wakaba 1.173 q<]>,
3035 wakaba 1.183 length $self->{ct}->{data});
3036 wakaba 1.173
3037 wakaba 1.165 ## Stay in the state.
3038     !!!next-input-character;
3039     redo A;
3040     }
3041 wakaba 1.127
3042 wakaba 1.165 ## ISSUE: "text tokens" in spec.
3043     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3044 wakaba 1.183 if ($self->{nc} == 0x005D) { # ]
3045 wakaba 1.165 !!!cp (221.5);
3046     $self->{state} = CDATA_SECTION_MSE2_STATE;
3047     !!!next-input-character;
3048     redo A;
3049     } else {
3050     !!!cp (221.6);
3051 wakaba 1.183 $self->{ct}->{data} .= ']';
3052 wakaba 1.165 $self->{state} = CDATA_SECTION_STATE;
3053     ## Reconsume.
3054     redo A;
3055     }
3056     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3057 wakaba 1.183 if ($self->{nc} == 0x003E) { # >
3058 wakaba 1.165 $self->{state} = DATA_STATE;
3059     !!!next-input-character;
3060 wakaba 1.183 if (length $self->{ct}->{data}) { # character
3061 wakaba 1.165 !!!cp (221.7);
3062 wakaba 1.183 !!!emit ($self->{ct}); # character
3063 wakaba 1.127 } else {
3064 wakaba 1.165 !!!cp (221.8);
3065 wakaba 1.183 ## No token to emit. $self->{ct} is discarded.
3066 wakaba 1.127 }
3067 wakaba 1.165 redo A;
3068 wakaba 1.183 } elsif ($self->{nc} == 0x005D) { # ]
3069 wakaba 1.165 !!!cp (221.9); # character
3070 wakaba 1.183 $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3071 wakaba 1.165 ## Stay in the state.
3072 wakaba 1.127 !!!next-input-character;
3073 wakaba 1.165 redo A;
3074 wakaba 1.127 } else {
3075 wakaba 1.165 !!!cp (221.11);
3076 wakaba 1.183 $self->{ct}->{data} .= ']]'; # character
3077 wakaba 1.165 $self->{state} = CDATA_SECTION_STATE;
3078     ## Reconsume.
3079     redo A;
3080 wakaba 1.127 }
3081 wakaba 1.167 } elsif ($self->{state} == ENTITY_STATE) {
3082 wakaba 1.187 if ($is_space->{$self->{nc}} or
3083     {
3084     0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3085     $self->{entity_add} => 1,
3086     }->{$self->{nc}}) {
3087 wakaba 1.168 !!!cp (1001);
3088     ## Don't consume
3089     ## No error
3090     ## Return nothing.
3091     #
3092 wakaba 1.183 } elsif ($self->{nc} == 0x0023) { # #
3093 wakaba 1.170 !!!cp (999);
3094 wakaba 1.168 $self->{state} = ENTITY_HASH_STATE;
3095 wakaba 1.183 $self->{s_kwd} = '#';
3096 wakaba 1.168 !!!next-input-character;
3097     redo A;
3098 wakaba 1.183 } elsif ((0x0041 <= $self->{nc} and
3099     $self->{nc} <= 0x005A) or # A..Z
3100     (0x0061 <= $self->{nc} and
3101     $self->{nc} <= 0x007A)) { # a..z
3102 wakaba 1.170 !!!cp (998);
3103 wakaba 1.168 require Whatpm::_NamedEntityList;
3104     $self->{state} = ENTITY_NAME_STATE;
3105 wakaba 1.183 $self->{s_kwd} = chr $self->{nc};
3106     $self->{entity__value} = $self->{s_kwd};
3107 wakaba 1.168 $self->{entity__match} = 0;
3108     !!!next-input-character;
3109     redo A;
3110     } else {
3111     !!!cp (1027);
3112     !!!parse-error (type => 'bare ero');
3113     ## Return nothing.
3114     #
3115     }
3116 wakaba 1.20
3117 wakaba 1.168 ## NOTE: No character is consumed by the "consume a character
3118     ## reference" algorithm. In other word, there is an "&" character
3119     ## that does not introduce a character reference, which would be
3120     ## appended to the parent element or the attribute value in later
3121     ## process of the tokenizer.
3122 wakaba 1.112
3123 wakaba 1.169 if ($self->{prev_state} == DATA_STATE) {
3124 wakaba 1.170 !!!cp (997);
3125 wakaba 1.169 $self->{state} = $self->{prev_state};
3126 wakaba 1.168 ## Reconsume.
3127     !!!emit ({type => CHARACTER_TOKEN, data => '&',
3128     line => $self->{line_prev},
3129     column => $self->{column_prev},
3130     });
3131     redo A;
3132 wakaba 1.169 } else {
3133 wakaba 1.170 !!!cp (996);
3134 wakaba 1.183 $self->{ca}->{value} .= '&';
3135 wakaba 1.169 $self->{state} = $self->{prev_state};
3136     ## Reconsume.
3137     redo A;
3138 wakaba 1.168 }
3139     } elsif ($self->{state} == ENTITY_HASH_STATE) {
3140 wakaba 1.183 if ($self->{nc} == 0x0078 or # x
3141     $self->{nc} == 0x0058) { # X
3142 wakaba 1.170 !!!cp (995);
3143 wakaba 1.168 $self->{state} = HEXREF_X_STATE;
3144 wakaba 1.183 $self->{s_kwd} .= chr $self->{nc};
3145 wakaba 1.168 !!!next-input-character;
3146     redo A;
3147 wakaba 1.183 } elsif (0x0030 <= $self->{nc} and
3148     $self->{nc} <= 0x0039) { # 0..9
3149 wakaba 1.170 !!!cp (994);
3150 wakaba 1.168 $self->{state} = NCR_NUM_STATE;
3151 wakaba 1.183 $self->{s_kwd} = $self->{nc} - 0x0030;
3152 wakaba 1.168 !!!next-input-character;
3153     redo A;
3154     } else {
3155     !!!parse-error (type => 'bare nero',
3156     line => $self->{line_prev},
3157     column => $self->{column_prev} - 1);
3158    
3159     ## NOTE: According to the spec algorithm, nothing is returned,
3160     ## and then "&#" is appended to the parent element or the attribute
3161     ## value in the later processing.
3162    
3163 wakaba 1.169 if ($self->{prev_state} == DATA_STATE) {
3164 wakaba 1.170 !!!cp (1019);
3165 wakaba 1.169 $self->{state} = $self->{prev_state};
3166 wakaba 1.168 ## Reconsume.
3167     !!!emit ({type => CHARACTER_TOKEN,
3168     data => '&#',
3169     line => $self->{line_prev},
3170     column => $self->{column_prev} - 1,
3171     });
3172     redo A;
3173 wakaba 1.169 } else {
3174 wakaba 1.170 !!!cp (993);
3175 wakaba 1.183 $self->{ca}->{value} .= '&#';
3176 wakaba 1.169 $self->{state} = $self->{prev_state};
3177     ## Reconsume.
3178     redo A;
3179 wakaba 1.1 }
3180 wakaba 1.168 }
3181     } elsif ($self->{state} == NCR_NUM_STATE) {
3182 wakaba 1.183 if (0x0030 <= $self->{nc} and
3183     $self->{nc} <= 0x0039) { # 0..9
3184 wakaba 1.78 !!!cp (1012);
3185 wakaba 1.183 $self->{s_kwd} *= 10;
3186     $self->{s_kwd} += $self->{nc} - 0x0030;
3187 wakaba 1.1
3188 wakaba 1.168 ## Stay in the state.
3189 wakaba 1.1 !!!next-input-character;
3190 wakaba 1.168 redo A;
3191 wakaba 1.183 } elsif ($self->{nc} == 0x003B) { # ;
3192 wakaba 1.78 !!!cp (1013);
3193 wakaba 1.1 !!!next-input-character;
3194 wakaba 1.168 #
3195 wakaba 1.1 } else {
3196 wakaba 1.78 !!!cp (1014);
3197 wakaba 1.168 !!!parse-error (type => 'no refc');
3198     ## Reconsume.
3199     #
3200 wakaba 1.1 }
3201    
3202 wakaba 1.183 my $code = $self->{s_kwd};
3203 wakaba 1.168 my $l = $self->{line_prev};
3204     my $c = $self->{column_prev};
3205 wakaba 1.191 if ($charref_map->{$code}) {
3206 wakaba 1.78 !!!cp (1015);
3207 wakaba 1.153 !!!parse-error (type => 'invalid character reference',
3208     text => (sprintf 'U+%04X', $code),
3209     line => $l, column => $c);
3210 wakaba 1.191 $code = $charref_map->{$code};
3211 wakaba 1.26 } elsif ($code > 0x10FFFF) {
3212 wakaba 1.78 !!!cp (1016);
3213 wakaba 1.153 !!!parse-error (type => 'invalid character reference',
3214     text => (sprintf 'U-%08X', $code),
3215     line => $l, column => $c);
3216 wakaba 1.26 $code = 0xFFFD;
3217 wakaba 1.1 }
3218 wakaba 1.168
3219 wakaba 1.169 if ($self->{prev_state} == DATA_STATE) {
3220 wakaba 1.170 !!!cp (992);
3221 wakaba 1.169 $self->{state} = $self->{prev_state};
3222 wakaba 1.168 ## Reconsume.
3223 wakaba 1.169 !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3224     line => $l, column => $c,
3225     });
3226 wakaba 1.168 redo A;
3227     } else {
3228 wakaba 1.170 !!!cp (991);
3229 wakaba 1.183 $self->{ca}->{value} .= chr $code;
3230     $self->{ca}->{has_reference} = 1;
3231 wakaba 1.169 $self->{state} = $self->{prev_state};
3232 wakaba 1.168 ## Reconsume.
3233     redo A;
3234     }
3235     } elsif ($self->{state} == HEXREF_X_STATE) {
3236 wakaba 1.183 if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3237     (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3238     (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3239 wakaba 1.168 # 0..9, A..F, a..f
3240 wakaba 1.170 !!!cp (990);
3241 wakaba 1.168 $self->{state} = HEXREF_HEX_STATE;
3242 wakaba 1.183 $self->{s_kwd} = 0;
3243 wakaba 1.168 ## Reconsume.
3244     redo A;
3245     } else {
3246     !!!parse-error (type => 'bare hcro',
3247     line => $self->{line_prev},
3248     column => $self->{column_prev} - 2);
3249    
3250     ## NOTE: According to the spec algorithm, nothing is returned,
3251     ## and then "&#" followed by "X" or "x" is appended to the parent
3252     ## element or the attribute value in the later processing.
3253    
3254 wakaba 1.169 if ($self->{prev_state} == DATA_STATE) {
3255 wakaba 1.170 !!!cp (1005);
3256 wakaba 1.169 $self->{state} = $self->{prev_state};
3257 wakaba 1.168 ## Reconsume.
3258     !!!emit ({type => CHARACTER_TOKEN,
3259 wakaba 1.183 data => '&' . $self->{s_kwd},
3260 wakaba 1.168 line => $self->{line_prev},
3261 wakaba 1.183 column => $self->{column_prev} - length $self->{s_kwd},
3262 wakaba 1.168 });
3263     redo A;
3264 wakaba 1.169 } else {
3265 wakaba 1.170 !!!cp (989);
3266 wakaba 1.183 $self->{ca}->{value} .= '&' . $self->{s_kwd};
3267 wakaba 1.169 $self->{state} = $self->{prev_state};
3268     ## Reconsume.
3269     redo A;
3270 wakaba 1.168 }
3271     }
3272     } elsif ($self->{state} == HEXREF_HEX_STATE) {
3273 wakaba 1.183 if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3274 wakaba 1.168 # 0..9
3275     !!!cp (1002);
3276 wakaba 1.183 $self->{s_kwd} *= 0x10;
3277     $self->{s_kwd} += $self->{nc} - 0x0030;
3278 wakaba 1.168 ## Stay in the state.
3279     !!!next-input-character;
3280     redo A;
3281 wakaba 1.183 } elsif (0x0061 <= $self->{nc} and
3282     $self->{nc} <= 0x0066) { # a..f
3283 wakaba 1.168 !!!cp (1003);
3284 wakaba 1.183 $self->{s_kwd} *= 0x10;
3285     $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3286 wakaba 1.168 ## Stay in the state.
3287     !!!next-input-character;
3288     redo A;
3289 wakaba 1.183 } elsif (0x0041 <= $self->{nc} and
3290     $self->{nc} <= 0x0046) { # A..F
3291 wakaba 1.168 !!!cp (1004);
3292 wakaba 1.183 $self->{s_kwd} *= 0x10;
3293     $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3294 wakaba 1.168 ## Stay in the state.
3295     !!!next-input-character;
3296     redo A;
3297 wakaba 1.183 } elsif ($self->{nc} == 0x003B) { # ;
3298 wakaba 1.168 !!!cp (1006);
3299     !!!next-input-character;
3300     #
3301     } else {
3302     !!!cp (1007);
3303     !!!parse-error (type => 'no refc',
3304     line => $self->{line},
3305     column => $self->{column});
3306     ## Reconsume.
3307     #
3308     }
3309    
3310 wakaba 1.183 my $code = $self->{s_kwd};
3311 wakaba 1.168 my $l = $self->{line_prev};
3312     my $c = $self->{column_prev};
3313 wakaba 1.191 if ($charref_map->{$code}) {
3314 wakaba 1.168 !!!cp (1008);
3315     !!!parse-error (type => 'invalid character reference',
3316     text => (sprintf 'U+%04X', $code),
3317     line => $l, column => $c);
3318 wakaba 1.191 $code = $charref_map->{$code};
3319 wakaba 1.168 } elsif ($code > 0x10FFFF) {
3320     !!!cp (1009);
3321     !!!parse-error (type => 'invalid character reference',
3322     text => (sprintf 'U-%08X', $code),
3323     line => $l, column => $c);
3324     $code = 0xFFFD;
3325     }
3326    
3327 wakaba 1.169 if ($self->{prev_state} == DATA_STATE) {
3328 wakaba 1.170 !!!cp (988);
3329 wakaba 1.169 $self->{state} = $self->{prev_state};
3330 wakaba 1.168 ## Reconsume.
3331 wakaba 1.169 !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3332     line => $l, column => $c,
3333     });
3334 wakaba 1.168 redo A;
3335     } else {
3336 wakaba 1.170 !!!cp (987);
3337 wakaba 1.183 $self->{ca}->{value} .= chr $code;
3338     $self->{ca}->{has_reference} = 1;
3339 wakaba 1.169 $self->{state} = $self->{prev_state};
3340 wakaba 1.168 ## Reconsume.
3341     redo A;
3342     }
3343     } elsif ($self->{state} == ENTITY_NAME_STATE) {
3344 wakaba 1.183 if (length $self->{s_kwd} < 30 and
3345 wakaba 1.168 ## NOTE: Some number greater than the maximum length of entity name
3346 wakaba 1.183 ((0x0041 <= $self->{nc} and # a
3347     $self->{nc} <= 0x005A) or # x
3348     (0x0061 <= $self->{nc} and # a
3349     $self->{nc} <= 0x007A) or # z
3350     (0x0030 <= $self->{nc} and # 0
3351     $self->{nc} <= 0x0039) or # 9
3352     $self->{nc} == 0x003B)) { # ;
3353 wakaba 1.168 our $EntityChar;
3354 wakaba 1.183 $self->{s_kwd} .= chr $self->{nc};
3355     if (defined $EntityChar->{$self->{s_kwd}}) {
3356     if ($self->{nc} == 0x003B) { # ;
3357 wakaba 1.168 !!!cp (1020);
3358 wakaba 1.183 $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3359 wakaba 1.168 $self->{entity__match} = 1;
3360     !!!next-input-character;
3361     #
3362     } else {
3363     !!!cp (1021);
3364 wakaba 1.183 $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3365 wakaba 1.168 $self->{entity__match} = -1;
3366     ## Stay in the state.
3367     !!!next-input-character;
3368     redo A;
3369     }
3370     } else {
3371     !!!cp (1022);
3372 wakaba 1.183 $self->{entity__value} .= chr $self->{nc};
3373 wakaba 1.168 $self->{entity__match} *= 2;
3374     ## Stay in the state.
3375 wakaba 1.16 !!!next-input-character;
3376 wakaba 1.168 redo A;
3377     }
3378     }
3379    
3380     my $data;
3381     my $has_ref;
3382     if ($self->{entity__match} > 0) {
3383     !!!cp (1023);
3384     $data = $self->{entity__value};
3385     $has_ref = 1;
3386     #
3387     } elsif ($self->{entity__match} < 0) {
3388     !!!parse-error (type => 'no refc');
3389 wakaba 1.169 if ($self->{prev_state} != DATA_STATE and # in attribute
3390     $self->{entity__match} < -1) {
3391 wakaba 1.168 !!!cp (1024);
3392 wakaba 1.183 $data = '&' . $self->{s_kwd};
3393 wakaba 1.168 #
3394 wakaba 1.37 } else {
3395 wakaba 1.168 !!!cp (1025);
3396     $data = $self->{entity__value};
3397     $has_ref = 1;
3398     #
3399 wakaba 1.16 }
3400 wakaba 1.1 } else {
3401 wakaba 1.168 !!!cp (1026);
3402     !!!parse-error (type => 'bare ero',
3403     line => $self->{line_prev},
3404 wakaba 1.183 column => $self->{column_prev} - length $self->{s_kwd});
3405     $data = '&' . $self->{s_kwd};
3406 wakaba 1.168 #
3407 wakaba 1.1 }
3408 wakaba 1.168
3409     ## NOTE: In these cases, when a character reference is found,
3410     ## it is consumed and a character token is returned, or, otherwise,
3411     ## nothing is consumed and returned, according to the spec algorithm.
3412     ## In this implementation, anything that has been examined by the
3413     ## tokenizer is appended to the parent element or the attribute value
3414     ## as string, either literal string when no character reference or
3415     ## entity-replaced string otherwise, in this stage, since any characters
3416     ## that would not be consumed are appended in the data state or in an
3417     ## appropriate attribute value state anyway.
3418    
3419 wakaba 1.169 if ($self->{prev_state} == DATA_STATE) {
3420 wakaba 1.170 !!!cp (986);
3421 wakaba 1.169 $self->{state} = $self->{prev_state};
3422 wakaba 1.168 ## Reconsume.
3423     !!!emit ({type => CHARACTER_TOKEN,
3424 wakaba 1.169 data => $data,
3425 wakaba 1.168 line => $self->{line_prev},
3426 wakaba 1.183 column => $self->{column_prev} + 1 - length $self->{s_kwd},
3427 wakaba 1.168 });
3428 wakaba 1.167 redo A;
3429 wakaba 1.169 } else {
3430 wakaba 1.170 !!!cp (985);
3431 wakaba 1.183 $self->{ca}->{value} .= $data;
3432     $self->{ca}->{has_reference} = 1 if $has_ref;
3433 wakaba 1.169 $self->{state} = $self->{prev_state};
3434     ## Reconsume.
3435     redo A;
3436 wakaba 1.37 }
3437 wakaba 1.1 } else {
3438 wakaba 1.167 die "$0: $self->{state}: Unknown state";
3439     }
3440     } # A
3441    
3442     die "$0: _get_next_token: unexpected case";
3443     } # _get_next_token
3444 wakaba 1.1
3445     sub _initialize_tree_constructor ($) {
3446     my $self = shift;
3447     ## NOTE: $self->{document} MUST be specified before this method is called
3448     $self->{document}->strict_error_checking (0);
3449     ## TODO: Turn mutation events off # MUST
3450     ## TODO: Turn loose Document option (manakai extension) on
3451 wakaba 1.18 $self->{document}->manakai_is_html (1); # MUST
3452 wakaba 1.154 $self->{document}->set_user_data (manakai_source_line => 1);
3453     $self->{document}->set_user_data (manakai_source_column => 1);
3454 wakaba 1.1 } # _initialize_tree_constructor
3455    
3456     sub _terminate_tree_constructor ($) {
3457     my $self = shift;
3458     $self->{document}->strict_error_checking (1);
3459     ## TODO: Turn mutation events on
3460     } # _terminate_tree_constructor
3461    
3462     ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?
3463    
3464 wakaba 1.3 { # tree construction stage
3465     my $token;
3466    
3467 wakaba 1.1 sub _construct_tree ($) {
3468     my ($self) = @_;
3469    
3470     ## When an interactive UA render the $self->{document} available
3471     ## to the user, or when it begin accepting user input, are
3472     ## not defined.
3473    
3474     !!!next-token;
3475    
3476 wakaba 1.3 undef $self->{form_element};
3477     undef $self->{head_element};
3478 wakaba 1.202 undef $self->{head_element_inserted};
3479 wakaba 1.3 $self->{open_elements} = [];
3480     undef $self->{inner_html_node};
3481    
3482 wakaba 1.84 ## NOTE: The "initial" insertion mode.
3483 wakaba 1.3 $self->_tree_construction_initial; # MUST
3484 wakaba 1.84
3485     ## NOTE: The "before html" insertion mode.
3486 wakaba 1.3 $self->_tree_construction_root_element;
3487 wakaba 1.84 $self->{insertion_mode} = BEFORE_HEAD_IM;
3488    
3489     ## NOTE: The "before head" insertion mode and so on.
3490 wakaba 1.3 $self->_tree_construction_main;
3491     } # _construct_tree
3492    
3493     sub _tree_construction_initial ($) {
3494     my $self = shift;
3495 wakaba 1.84
3496     ## NOTE: "initial" insertion mode
3497    
3498 wakaba 1.18 INITIAL: {
3499 wakaba 1.55 if ($token->{type} == DOCTYPE_TOKEN) {
3500 wakaba 1.18 ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3501     ## error, switch to a conformance checking mode for another
3502     ## language.
3503     my $doctype_name = $token->{name};
3504     $doctype_name = '' unless defined $doctype_name;
3505 wakaba 1.159 $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506 wakaba 1.18 if (not defined $token->{name} or # <!DOCTYPE>
3507 wakaba 1.183 defined $token->{sysid}) {
3508 wakaba 1.79 !!!cp ('t1');
3509 wakaba 1.113 !!!parse-error (type => 'not HTML5', token => $token);
3510 wakaba 1.18 } elsif ($doctype_name ne 'HTML') {
3511 wakaba 1.79 !!!cp ('t2');
3512 wakaba 1.113 !!!parse-error (type => 'not HTML5', token => $token);
3513 wakaba 1.183 } elsif (defined $token->{pubid}) {
3514     if ($token->{pubid} eq 'XSLT-compat') {
3515 wakaba 1.159 !!!cp ('t1.2');
3516     !!!parse-error (type => 'XSLT-compat', token => $token,
3517     level => $self->{level}->{should});
3518     } else {
3519     !!!parse-error (type => 'not HTML5', token => $token);
3520     }
3521 wakaba 1.79 } else {
3522     !!!cp ('t3');
3523 wakaba 1.159 #
3524 wakaba 1.18 }
3525    
3526     my $doctype = $self->{document}->create_document_type_definition
3527     ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528 wakaba 1.122 ## NOTE: Default value for both |public_id| and |system_id| attributes
3529     ## are empty strings, so that we don't set any value in missing cases.
3530 wakaba 1.183 $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531     $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3532 wakaba 1.18 ## NOTE: Other DocumentType attributes are null or empty lists.
3533     ## ISSUE: internalSubset = null??
3534     $self->{document}->append_child ($doctype);
3535    
3536 wakaba 1.75 if ($token->{quirks} or $doctype_name ne 'HTML') {
3537 wakaba 1.79 !!!cp ('t4');
3538 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
3539 wakaba 1.183 } elsif (defined $token->{pubid}) {
3540     my $pubid = $token->{pubid};
3541 wakaba 1.18 $pubid =~ tr/a-z/A-z/;
3542 wakaba 1.143 my $prefix = [
3543     "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544     "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545     "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546     "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547     "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548     "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549     "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550     "-//IETF//DTD HTML 2.0 STRICT//",
3551     "-//IETF//DTD HTML 2.0//",
3552     "-//IETF//DTD HTML 2.1E//",
3553     "-//IETF//DTD HTML 3.0//",
3554     "-//IETF//DTD HTML 3.2 FINAL//",
3555     "-//IETF//DTD HTML 3.2//",
3556     "-//IETF//DTD HTML 3//",
3557     "-//IETF//DTD HTML LEVEL 0//",
3558     "-//IETF//DTD HTML LEVEL 1//",
3559     "-//IETF//DTD HTML LEVEL 2//",
3560     "-//IETF//DTD HTML LEVEL 3//",
3561     "-//IETF//DTD HTML STRICT LEVEL 0//",
3562     "-//IETF//DTD HTML STRICT LEVEL 1//",
3563     "-//IETF//DTD HTML STRICT LEVEL 2//",
3564     "-//IETF//DTD HTML STRICT LEVEL 3//",
3565     "-//IETF//DTD HTML STRICT//",
3566     "-//IETF//DTD HTML//",
3567     "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574     "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575     "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576     "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579     "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580     "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581     "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582     "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585     "-//W3C//DTD HTML 3 1995-03-24//",
3586     "-//W3C//DTD HTML 3.2 DRAFT//",
3587     "-//W3C//DTD HTML 3.2 FINAL//",
3588     "-//W3C//DTD HTML 3.2//",
3589     "-//W3C//DTD HTML 3.2S DRAFT//",
3590     "-//W3C//DTD HTML 4.0 FRAMESET//",
3591     "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592     "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593     "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594     "-//W3C//DTD W3 HTML//",
3595     "-//W3O//DTD W3 HTML 3.0//",
3596     "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597     "-//WEBTECHS//DTD MOZILLA HTML//",
3598     ]; # $prefix
3599     my $match;
3600     for (@$prefix) {
3601     if (substr ($prefix, 0, length $_) eq $_) {
3602     $match = 1;
3603     last;
3604     }
3605     }
3606     if ($match or
3607     $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608     $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609     $pubid eq "HTML") {
3610 wakaba 1.79 !!!cp ('t5');
3611 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
3612 wakaba 1.143 } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613     $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614 wakaba 1.183 if (defined $token->{sysid}) {
3615 wakaba 1.79 !!!cp ('t6');
3616 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
3617     } else {
3618 wakaba 1.79 !!!cp ('t7');
3619 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
3620 wakaba 1.3 }
3621 wakaba 1.143 } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622     $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623 wakaba 1.79 !!!cp ('t8');
3624 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
3625 wakaba 1.79 } else {
3626     !!!cp ('t9');
3627 wakaba 1.18 }
3628 wakaba 1.79 } else {
3629     !!!cp ('t10');
3630 wakaba 1.18 }
3631 wakaba 1.183 if (defined $token->{sysid}) {
3632     my $sysid = $token->{sysid};
3633 wakaba 1.18 $sysid =~ tr/A-Z/a-z/;
3634     if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635 wakaba 1.143 ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636     ## marked as quirks.
3637 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
3638 wakaba 1.79 !!!cp ('t11');
3639     } else {
3640     !!!cp ('t12');
3641 wakaba 1.18 }
3642 wakaba 1.79 } else {
3643     !!!cp ('t13');
3644 wakaba 1.18 }
3645    
3646 wakaba 1.84 ## Go to the "before html" insertion mode.
3647 wakaba 1.18 !!!next-token;
3648     return;
3649     } elsif ({
3650 wakaba 1.55 START_TAG_TOKEN, 1,
3651     END_TAG_TOKEN, 1,
3652     END_OF_FILE_TOKEN, 1,
3653 wakaba 1.18 }->{$token->{type}}) {
3654 wakaba 1.79 !!!cp ('t14');
3655 wakaba 1.113 !!!parse-error (type => 'no DOCTYPE', token => $token);
3656 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
3657 wakaba 1.84 ## Go to the "before html" insertion mode.
3658 wakaba 1.18 ## reprocess
3659 wakaba 1.125 !!!ack-later;
3660 wakaba 1.18 return;
3661 wakaba 1.55 } elsif ($token->{type} == CHARACTER_TOKEN) {
3662 wakaba 1.188 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663 wakaba 1.18 ## Ignore the token
3664 wakaba 1.26
3665 wakaba 1.18 unless (length $token->{data}) {
3666 wakaba 1.79 !!!cp ('t15');
3667 wakaba 1.84 ## Stay in the insertion mode.
3668 wakaba 1.18 !!!next-token;
3669     redo INITIAL;
3670 wakaba 1.79 } else {
3671     !!!cp ('t16');
3672 wakaba 1.3 }
3673 wakaba 1.79 } else {
3674     !!!cp ('t17');
3675 wakaba 1.3 }
3676 wakaba 1.18
3677 wakaba 1.113 !!!parse-error (type => 'no DOCTYPE', token => $token);
3678 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
3679 wakaba 1.84 ## Go to the "before html" insertion mode.
3680 wakaba 1.18 ## reprocess
3681     return;
3682 wakaba 1.55 } elsif ($token->{type} == COMMENT_TOKEN) {
3683 wakaba 1.79 !!!cp ('t18');
3684 wakaba 1.18 my $comment = $self->{document}->create_comment ($token->{data});
3685     $self->{document}->append_child ($comment);
3686    
3687 wakaba 1.84 ## Stay in the insertion mode.
3688 wakaba 1.18 !!!next-token;
3689     redo INITIAL;
3690     } else {
3691 wakaba 1.55 die "$0: $token->{type}: Unknown token type";
3692 wakaba 1.18 }
3693     } # INITIAL
3694 wakaba 1.79
3695     die "$0: _tree_construction_initial: This should be never reached";
3696 wakaba 1.3 } # _tree_construction_initial
3697    
3698     sub _tree_construction_root_element ($) {
3699     my $self = shift;
3700 wakaba 1.84
3701     ## NOTE: "before html" insertion mode.
3702 wakaba 1.3
3703     B: {
3704 wakaba 1.55 if ($token->{type} == DOCTYPE_TOKEN) {
3705 wakaba 1.79 !!!cp ('t19');
3706 wakaba 1.153 !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3707 wakaba 1.3 ## Ignore the token
3708 wakaba 1.84 ## Stay in the insertion mode.
3709 wakaba 1.3 !!!next-token;
3710     redo B;
3711 wakaba 1.55 } elsif ($token->{type} == COMMENT_TOKEN) {
3712 wakaba 1.79 !!!cp ('t20');
3713 wakaba 1.3 my $comment = $self->{document}->create_comment ($token->{data});
3714     $self->{document}->append_child ($comment);
3715 wakaba 1.84 ## Stay in the insertion mode.
3716 wakaba 1.3 !!!next-token;
3717     redo B;
3718 wakaba 1.55 } elsif ($token->{type} == CHARACTER_TOKEN) {
3719 wakaba 1.188 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720 wakaba 1.26 ## Ignore the token.
3721    
3722 wakaba 1.3 unless (length $token->{data}) {
3723 wakaba 1.79 !!!cp ('t21');
3724 wakaba 1.84 ## Stay in the insertion mode.
3725 wakaba 1.3 !!!next-token;
3726     redo B;
3727 wakaba 1.79 } else {
3728     !!!cp ('t22');
3729 wakaba 1.3 }
3730 wakaba 1.79 } else {
3731     !!!cp ('t23');
3732 wakaba 1.3 }
3733 wakaba 1.61
3734     $self->{application_cache_selection}->(undef);
3735    
3736     #
3737     } elsif ($token->{type} == START_TAG_TOKEN) {
3738 wakaba 1.84 if ($token->{tag_name} eq 'html') {
3739     my $root_element;
3740 wakaba 1.126 !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741 wakaba 1.84 $self->{document}->append_child ($root_element);
3742 wakaba 1.123 push @{$self->{open_elements}},
3743     [$root_element, $el_category->{html}];
3744 wakaba 1.84
3745     if ($token->{attributes}->{manifest}) {
3746     !!!cp ('t24');
3747     $self->{application_cache_selection}
3748     ->($token->{attributes}->{manifest}->{value});
3749 wakaba 1.118 ## ISSUE: Spec is unclear on relative references.
3750     ## According to Hixie (#whatwg 2008-03-19), it should be
3751     ## resolved against the base URI of the document in HTML
3752     ## or xml:base of the element in XHTML.
3753 wakaba 1.84 } else {
3754     !!!cp ('t25');
3755     $self->{application_cache_selection}->(undef);
3756     }
3757    
3758 wakaba 1.125 !!!nack ('t25c');
3759    
3760 wakaba 1.84 !!!next-token;
3761     return; ## Go to the "before head" insertion mode.
3762 wakaba 1.61 } else {
3763 wakaba 1.84 !!!cp ('t25.1');
3764     #
3765 wakaba 1.61 }
3766 wakaba 1.3 } elsif ({
3767 wakaba 1.55 END_TAG_TOKEN, 1,
3768     END_OF_FILE_TOKEN, 1,
3769 wakaba 1.3 }->{$token->{type}}) {
3770 wakaba 1.79 !!!cp ('t26');
3771 wakaba 1.3 #
3772     } else {
3773 wakaba 1.55 die "$0: $token->{type}: Unknown token type";
3774 wakaba 1.3 }
3775 wakaba 1.61
3776 wakaba 1.126 my $root_element;
3777     !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778 wakaba 1.84 $self->{document}->append_child ($root_element);
3779 wakaba 1.123 push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780 wakaba 1.84
3781     $self->{application_cache_selection}->(undef);
3782    
3783     ## NOTE: Reprocess the token.
3784 wakaba 1.125 !!!ack-later;
3785 wakaba 1.84 return; ## Go to the "before head" insertion mode.
3786 wakaba 1.3 } # B
3787 wakaba 1.79
3788     die "$0: _tree_construction_root_element: This should never be reached";
3789 wakaba 1.3 } # _tree_construction_root_element
3790    
3791     sub _reset_insertion_mode ($) {
3792     my $self = shift;
3793    
3794     ## Step 1
3795     my $last;
3796    
3797     ## Step 2
3798     my $i = -1;
3799     my $node = $self->{open_elements}->[$i];
3800    
3801     ## Step 3
3802     S3: {
3803 wakaba 1.29 if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3804     $last = 1;
3805     if (defined $self->{inner_html_node}) {
3806 wakaba 1.140 !!!cp ('t28');
3807     $node = $self->{inner_html_node};
3808     } else {
3809     die "_reset_insertion_mode: t27";
3810 wakaba 1.3 }
3811     }
3812 wakaba 1.140
3813     ## Step 4..14
3814     my $new_mode;
3815     if ($node->[1] & FOREIGN_EL) {
3816     !!!cp ('t28.1');
3817     ## NOTE: Strictly spaking, the line below only applies to MathML and
3818     ## SVG elements. Currently the HTML syntax supports only MathML and
3819     ## SVG elements as foreigners.
3820 wakaba 1.148 $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3821 wakaba 1.140 } elsif ($node->[1] & TABLE_CELL_EL) {
3822     if ($last) {
3823     !!!cp ('t28.2');
3824     #
3825     } else {
3826     !!!cp ('t28.3');
3827     $new_mode = IN_CELL_IM;
3828     }
3829     } else {
3830     !!!cp ('t28.4');
3831     $new_mode = {
3832 wakaba 1.54 select => IN_SELECT_IM,
3833 wakaba 1.83 ## NOTE: |option| and |optgroup| do not set
3834     ## insertion mode to "in select" by themselves.
3835 wakaba 1.54 tr => IN_ROW_IM,
3836     tbody => IN_TABLE_BODY_IM,
3837     thead => IN_TABLE_BODY_IM,
3838     tfoot => IN_TABLE_BODY_IM,
3839     caption => IN_CAPTION_IM,
3840     colgroup => IN_COLUMN_GROUP_IM,
3841     table => IN_TABLE_IM,
3842     head => IN_BODY_IM, # not in head!
3843     body => IN_BODY_IM,
3844     frameset => IN_FRAMESET_IM,
3845 wakaba 1.123 }->{$node->[0]->manakai_local_name};
3846 wakaba 1.140 }
3847     $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3848 wakaba 1.3
3849 wakaba 1.126 ## Step 15
3850 wakaba 1.123 if ($node->[1] & HTML_EL) {
3851 wakaba 1.3 unless (defined $self->{head_element}) {
3852 wakaba 1.79 !!!cp ('t29');
3853 wakaba 1.54 $self->{insertion_mode} = BEFORE_HEAD_IM;
3854 wakaba 1.3 } else {
3855 wakaba 1.81 ## ISSUE: Can this state be reached?
3856 wakaba 1.79 !!!cp ('t30');
3857 wakaba 1.54 $self->{insertion_mode} = AFTER_HEAD_IM;
3858 wakaba 1.3 }
3859     return;
3860 wakaba 1.79 } else {
3861     !!!cp ('t31');
3862 wakaba 1.3 }
3863    
3864 wakaba 1.126 ## Step 16
3865 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM and return if $last;
3866 wakaba 1.3
3867 wakaba 1.126 ## Step 17
3868 wakaba 1.3 $i--;
3869     $node = $self->{open_elements}->[$i];
3870    
3871 wakaba 1.126 ## Step 18
3872 wakaba 1.3 redo S3;
3873     } # S3
3874 wakaba 1.79
3875     die "$0: _reset_insertion_mode: This line should never be reached";
3876 wakaba 1.3 } # _reset_insertion_mode
3877    
3878     sub _tree_construction_main ($) {
3879     my $self = shift;
3880    
3881 wakaba 1.1 my $active_formatting_elements = [];
3882    
3883     my $reconstruct_active_formatting_elements = sub { # MUST
3884     my $insert = shift;
3885    
3886     ## Step 1
3887     return unless @$active_formatting_elements;
3888    
3889     ## Step 3
3890     my $i = -1;
3891     my $entry = $active_formatting_elements->[$i];
3892    
3893     ## Step 2
3894     return if $entry->[0] eq '#marker';
3895 wakaba 1.3 for (@{$self->{open_elements}}) {
3896 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
3897 wakaba 1.79 !!!cp ('t32');
3898 wakaba 1.1 return;
3899     }
3900     }
3901    
3902     S4: {
3903     ## Step 4
3904     last S4 if $active_formatting_elements->[0]->[0] eq $entry->[0];
3905    
3906     ## Step 5
3907     $i--;
3908     $entry = $active_formatting_elements->[$i];
3909    
3910     ## Step 6
3911     if ($entry->[0] eq '#marker') {
3912 wakaba 1.81 !!!cp ('t33_1');
3913 wakaba 1.1 #
3914     } else {
3915     my $in_open_elements;
3916 wakaba 1.3 OE: for (@{$self->{open_elements}}) {
3917 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
3918 wakaba 1.79 !!!cp ('t33');
3919 wakaba 1.1 $in_open_elements = 1;
3920     last OE;
3921     }
3922     }
3923     if ($in_open_elements) {
3924 wakaba 1.79 !!!cp ('t34');
3925 wakaba 1.1 #
3926     } else {
3927 wakaba 1.81 ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3928 wakaba 1.79 !!!cp ('t35');
3929 wakaba 1.1 redo S4;
3930     }
3931     }
3932    
3933     ## Step 7
3934     $i++;
3935     $entry = $active_formatting_elements->[$i];
3936     } # S4
3937    
3938     S7: {
3939     ## Step 8
3940     my $clone = [$entry->[0]->clone_node (0), $entry->[1]];
3941    
3942     ## Step 9
3943     $insert->($clone->[0]);
3944 wakaba 1.3 push @{$self->{open_elements}}, $clone;
3945 wakaba 1.1
3946     ## Step 10
3947 wakaba 1.3 $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
3948 wakaba 1.1
3949     ## Step 11
3950     unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3951 wakaba 1.79 !!!cp ('t36');
3952 wakaba 1.1 ## Step 7'
3953     $i++;
3954     $entry = $active_formatting_elements->[$i];
3955    
3956     redo S7;
3957     }
3958 wakaba 1.79
3959     !!!cp ('t37');
3960 wakaba 1.1 } # S7
3961     }; # $reconstruct_active_formatting_elements
3962    
3963     my $clear_up_to_marker = sub {
3964     for (reverse 0..$#$active_formatting_elements) {
3965     if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3966 wakaba 1.79 !!!cp ('t38');
3967 wakaba 1.1 splice @$active_formatting_elements, $_;
3968     return;
3969     }
3970     }
3971 wakaba 1.79
3972     !!!cp ('t39');
3973 wakaba 1.1 }; # $clear_up_to_marker
3974    
3975 wakaba 1.96 my $insert;
3976    
3977     my $parse_rcdata = sub ($) {
3978     my ($content_model_flag) = @_;
3979 wakaba 1.25
3980     ## Step 1
3981     my $start_tag_name = $token->{tag_name};
3982 wakaba 1.205 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
3983 wakaba 1.25
3984     ## Step 2
3985 wakaba 1.40 $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3986 wakaba 1.13 delete $self->{escape}; # MUST
3987 wakaba 1.25
3988 wakaba 1.205 ## Step 3, 4
3989     $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
3990    
3991 wakaba 1.125 !!!nack ('t40.1');
3992 wakaba 1.1 !!!next-token;
3993 wakaba 1.25 }; # $parse_rcdata
3994 wakaba 1.1
3995 wakaba 1.96 my $script_start_tag = sub () {
3996 wakaba 1.205 ## Step 1
3997 wakaba 1.1 my $script_el;
3998 wakaba 1.126 !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
3999 wakaba 1.205
4000     ## Step 2
4001 wakaba 1.1 ## TODO: mark as "parser-inserted"
4002    
4003 wakaba 1.205 ## Step 3
4004     ## TODO: Mark as "already executed", if ...
4005    
4006     ## Step 4
4007     $insert->($script_el);
4008    
4009     ## ISSUE: $script_el is not put into the stack
4010     push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4011    
4012     ## Step 5
4013 wakaba 1.40 $self->{content_model} = CDATA_CONTENT_MODEL;
4014 wakaba 1.13 delete $self->{escape}; # MUST
4015 wakaba 1.1
4016 wakaba 1.205 ## Step 6-7
4017     $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4018 wakaba 1.25
4019 wakaba 1.205 !!!nack ('t40.2');
4020 wakaba 1.1 !!!next-token;
4021     }; # $script_start_tag
4022    
4023 wakaba 1.102 ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4024     ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4025 wakaba 1.202 ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4026 wakaba 1.102 my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4027    
4028 wakaba 1.1 my $formatting_end_tag = sub {
4029 wakaba 1.113 my $end_tag_token = shift;
4030     my $tag_name = $end_tag_token->{tag_name};
4031 wakaba 1.1
4032 wakaba 1.103 ## NOTE: The adoption agency algorithm (AAA).
4033 wakaba 1.102
4034 wakaba 1.1 FET: {
4035     ## Step 1
4036     my $formatting_element;
4037     my $formatting_element_i_in_active;
4038     AFE: for (reverse 0..$#$active_formatting_elements) {
4039 wakaba 1.123 if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4040     !!!cp ('t52');
4041     last AFE;
4042     } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4043     eq $tag_name) {
4044 wakaba 1.79 !!!cp ('t51');
4045 wakaba 1.1 $formatting_element = $active_formatting_elements->[$_];
4046     $formatting_element_i_in_active = $_;
4047     last AFE;
4048     }
4049     } # AFE
4050     unless (defined $formatting_element) {
4051 wakaba 1.79 !!!cp ('t53');
4052 wakaba 1.153 !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4053 wakaba 1.1 ## Ignore the token
4054     !!!next-token;
4055     return;
4056     }
4057     ## has an element in scope
4058     my $in_scope = 1;
4059     my $formatting_element_i_in_open;
4060 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4061     my $node = $self->{open_elements}->[$_];
4062 wakaba 1.1 if ($node->[0] eq $formatting_element->[0]) {
4063     if ($in_scope) {
4064 wakaba 1.79 !!!cp ('t54');
4065 wakaba 1.1 $formatting_element_i_in_open = $_;
4066     last INSCOPE;
4067     } else { # in open elements but not in scope
4068 wakaba 1.79 !!!cp ('t55');
4069 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
4070     text => $token->{tag_name},
4071 wakaba 1.113 token => $end_tag_token);
4072 wakaba 1.1 ## Ignore the token
4073     !!!next-token;
4074     return;
4075     }
4076 wakaba 1.123 } elsif ($node->[1] & SCOPING_EL) {
4077 wakaba 1.79 !!!cp ('t56');
4078 wakaba 1.1 $in_scope = 0;
4079     }
4080     } # INSCOPE
4081     unless (defined $formatting_element_i_in_open) {
4082 wakaba 1.79 !!!cp ('t57');
4083 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
4084     text => $token->{tag_name},
4085 wakaba 1.113 token => $end_tag_token);
4086 wakaba 1.1 pop @$active_formatting_elements; # $formatting_element
4087     !!!next-token; ## TODO: ok?
4088     return;
4089     }
4090 wakaba 1.3 if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4091 wakaba 1.79 !!!cp ('t58');
4092 wakaba 1.122 !!!parse-error (type => 'not closed',
4093 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
4094 wakaba 1.122 ->manakai_local_name,
4095 wakaba 1.113 token => $end_tag_token);
4096 wakaba 1.1 }
4097    
4098     ## Step 2
4099     my $furthest_block;
4100     my $furthest_block_i_in_open;
4101 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
4102     my $node = $self->{open_elements}->[$_];
4103 wakaba 1.123 if (not ($node->[1] & FORMATTING_EL) and
4104 wakaba 1.1 #not $phrasing_category->{$node->[1]} and
4105 wakaba 1.123 ($node->[1] & SPECIAL_EL or
4106     $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4107 wakaba 1.79 !!!cp ('t59');
4108 wakaba 1.1 $furthest_block = $node;
4109     $furthest_block_i_in_open = $_;
4110 wakaba 1.203 ## NOTE: The topmost (eldest) node.
4111 wakaba 1.1 } elsif ($node->[0] eq $formatting_element->[0]) {
4112 wakaba 1.79 !!!cp ('t60');
4113 wakaba 1.1 last OE;
4114     }
4115     } # OE
4116    
4117     ## Step 3
4118     unless (defined $furthest_block) { # MUST
4119 wakaba 1.79 !!!cp ('t61');
4120 wakaba 1.3 splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4121 wakaba 1.1 splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4122     !!!next-token;
4123     return;
4124     }
4125    
4126     ## Step 4
4127 wakaba 1.3 my $common_ancestor_node = $self->{open_elements}->[$formatting_element_i_in_open - 1];
4128 wakaba 1.1
4129     ## Step 5
4130     my $furthest_block_parent = $furthest_block->[0]->parent_node;
4131     if (defined $furthest_block_parent) {
4132 wakaba 1.79 !!!cp ('t62');
4133 wakaba 1.1 $furthest_block_parent->remove_child ($furthest_block->[0]);
4134     }
4135    
4136     ## Step 6
4137     my $bookmark_prev_el
4138     = $active_formatting_elements->[$formatting_element_i_in_active - 1]
4139     ->[0];
4140    
4141     ## Step 7
4142     my $node = $furthest_block;
4143     my $node_i_in_open = $furthest_block_i_in_open;
4144     my $last_node = $furthest_block;
4145     S7: {
4146     ## Step 1
4147     $node_i_in_open--;
4148 wakaba 1.3 $node = $self->{open_elements}->[$node_i_in_open];
4149 wakaba 1.1
4150     ## Step 2
4151     my $node_i_in_active;
4152     S7S2: {
4153     for (reverse 0..$#$active_formatting_elements) {
4154     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4155 wakaba 1.79 !!!cp ('t63');
4156 wakaba 1.1 $node_i_in_active = $_;
4157     last S7S2;
4158     }
4159     }
4160 wakaba 1.3 splice @{$self->{open_elements}}, $node_i_in_open, 1;
4161 wakaba 1.1 redo S7;
4162     } # S7S2
4163    
4164     ## Step 3
4165     last S7 if $node->[0] eq $formatting_element->[0];
4166    
4167     ## Step 4
4168     if ($last_node->[0] eq $furthest_block->[0]) {
4169 wakaba 1.79 !!!cp ('t64');
4170 wakaba 1.1 $bookmark_prev_el = $node->[0];
4171     }
4172    
4173     ## Step 5
4174     if ($node->[0]->has_child_nodes ()) {
4175 wakaba 1.79 !!!cp ('t65');
4176 wakaba 1.1 my $clone = [$node->[0]->clone_node (0), $node->[1]];
4177     $active_formatting_elements->[$node_i_in_active] = $clone;
4178 wakaba 1.3 $self->{open_elements}->[$node_i_in_open] = $clone;
4179 wakaba 1.1 $node = $clone;
4180     }
4181    
4182     ## Step 6
4183     $node->[0]->append_child ($last_node->[0]);
4184    
4185     ## Step 7
4186     $last_node = $node;
4187    
4188     ## Step 8
4189     redo S7;
4190     } # S7
4191    
4192     ## Step 8
4193 wakaba 1.123 if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4194 wakaba 1.102 my $foster_parent_element;
4195     my $next_sibling;
4196 wakaba 1.123 OE: for (reverse 0..$#{$self->{open_elements}}) {
4197     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4198 wakaba 1.102 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4199     if (defined $parent and $parent->node_type == 1) {
4200     !!!cp ('t65.1');
4201     $foster_parent_element = $parent;
4202     $next_sibling = $self->{open_elements}->[$_]->[0];
4203     } else {
4204     !!!cp ('t65.2');
4205     $foster_parent_element
4206     = $self->{open_elements}->[$_ - 1]->[0];
4207     }
4208     last OE;
4209     }
4210     } # OE
4211     $foster_parent_element = $self->{open_elements}->[0]->[0]
4212     unless defined $foster_parent_element;
4213     $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4214     $open_tables->[-1]->[1] = 1; # tainted
4215     } else {
4216     !!!cp ('t65.3');
4217     $common_ancestor_node->[0]->append_child ($last_node->[0]);
4218     }
4219 wakaba 1.1
4220     ## Step 9
4221     my $clone = [$formatting_element->[0]->clone_node (0),
4222     $formatting_element->[1]];
4223    
4224     ## Step 10
4225     my @cn = @{$furthest_block->[0]->child_nodes};
4226     $clone->[0]->append_child ($_) for @cn;
4227    
4228     ## Step 11
4229     $furthest_block->[0]->append_child ($clone->[0]);
4230    
4231     ## Step 12
4232     my $i;
4233     AFE: for (reverse 0..$#$active_formatting_elements) {
4234     if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4235 wakaba 1.79 !!!cp ('t66');
4236 wakaba 1.1 splice @$active_formatting_elements, $_, 1;
4237     $i-- and last AFE if defined $i;
4238     } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4239 wakaba 1.79 !!!cp ('t67');
4240 wakaba 1.1 $i = $_;
4241     }
4242     } # AFE
4243     splice @$active_formatting_elements, $i + 1, 0, $clone;
4244    
4245     ## Step 13
4246     undef $i;
4247 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
4248     if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4249 wakaba 1.79 !!!cp ('t68');
4250 wakaba 1.3 splice @{$self->{open_elements}}, $_, 1;
4251 wakaba 1.1 $i-- and last OE if defined $i;
4252 wakaba 1.3 } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4253 wakaba 1.79 !!!cp ('t69');
4254 wakaba 1.1 $i = $_;
4255     }
4256     } # OE
4257 wakaba 1.203 splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4258 wakaba 1.1
4259     ## Step 14
4260     redo FET;
4261     } # FET
4262     }; # $formatting_end_tag
4263    
4264 wakaba 1.96 $insert = my $insert_to_current = sub {
4265 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4266 wakaba 1.1 }; # $insert_to_current
4267    
4268     my $insert_to_foster = sub {
4269 wakaba 1.95 my $child = shift;
4270 wakaba 1.123 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4271 wakaba 1.95 # MUST
4272     my $foster_parent_element;
4273     my $next_sibling;
4274 wakaba 1.123 OE: for (reverse 0..$#{$self->{open_elements}}) {
4275     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4276 wakaba 1.3 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4277 wakaba 1.1 if (defined $parent and $parent->node_type == 1) {
4278 wakaba 1.79 !!!cp ('t70');
4279 wakaba 1.1 $foster_parent_element = $parent;
4280 wakaba 1.3 $next_sibling = $self->{open_elements}->[$_]->[0];
4281 wakaba 1.1 } else {
4282 wakaba 1.79 !!!cp ('t71');
4283 wakaba 1.1 $foster_parent_element
4284 wakaba 1.3 = $self->{open_elements}->[$_ - 1]->[0];
4285 wakaba 1.1 }
4286     last OE;
4287     }
4288     } # OE
4289 wakaba 1.3 $foster_parent_element = $self->{open_elements}->[0]->[0]
4290 wakaba 1.1 unless defined $foster_parent_element;
4291     $foster_parent_element->insert_before
4292     ($child, $next_sibling);
4293 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
4294     } else {
4295     !!!cp ('t72');
4296     $self->{open_elements}->[-1]->[0]->append_child ($child);
4297     }
4298 wakaba 1.1 }; # $insert_to_foster
4299    
4300 wakaba 1.204 ## NOTE: Insert a character (MUST): When a character is inserted, if
4301     ## the last node that was inserted by the parser is a Text node and
4302     ## the character has to be inserted after that node, then the
4303     ## character is appended to the Text node. However, if any other
4304     ## node is inserted by the parser, then a new Text node is created
4305     ## and the character is appended as that Text node. If I'm not
4306     ## wrong, for a parser with scripting disabled, there are only two
4307     ## cases where this occurs. One is the case where an element node
4308     ## is inserted to the |head| element. This is covered by using the
4309 wakaba 1.202 ## |$self->{head_element_inserted}| flag. Another is the case where
4310     ## an element or comment is inserted into the |table| subtree while
4311     ## foster parenting happens. This is covered by using the [2] flag
4312     ## of the |$open_tables| structure. All other cases are handled
4313     ## simply by calling |manakai_append_text| method.
4314    
4315 wakaba 1.204 ## TODO: |<body><script>document.write("a<br>");
4316     ## document.body.removeChild (document.body.lastChild);
4317     ## document.write ("b")</script>|
4318    
4319 wakaba 1.126 B: while (1) {
4320 wakaba 1.55 if ($token->{type} == DOCTYPE_TOKEN) {
4321 wakaba 1.79 !!!cp ('t73');
4322 wakaba 1.153 !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4323 wakaba 1.52 ## Ignore the token
4324     ## Stay in the phase
4325     !!!next-token;
4326 wakaba 1.126 next B;
4327 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN and
4328 wakaba 1.52 $token->{tag_name} eq 'html') {
4329 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4330 wakaba 1.79 !!!cp ('t79');
4331 wakaba 1.153 !!!parse-error (type => 'after html', text => 'html', token => $token);
4332 wakaba 1.54 $self->{insertion_mode} = AFTER_BODY_IM;
4333     } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4334 wakaba 1.79 !!!cp ('t80');
4335 wakaba 1.153 !!!parse-error (type => 'after html', text => 'html', token => $token);
4336 wakaba 1.54 $self->{insertion_mode} = AFTER_FRAMESET_IM;
4337 wakaba 1.79 } else {
4338     !!!cp ('t81');
4339 wakaba 1.52 }
4340    
4341 wakaba 1.84 !!!cp ('t82');
4342 wakaba 1.113 !!!parse-error (type => 'not first start tag', token => $token);
4343 wakaba 1.52 my $top_el = $self->{open_elements}->[0]->[0];
4344     for my $attr_name (keys %{$token->{attributes}}) {
4345     unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4346 wakaba 1.79 !!!cp ('t84');
4347 wakaba 1.52 $top_el->set_attribute_ns
4348     (undef, [undef, $attr_name],
4349     $token->{attributes}->{$attr_name}->{value});
4350     }
4351     }
4352 wakaba 1.125 !!!nack ('t84.1');
4353 wakaba 1.52 !!!next-token;
4354 wakaba 1.126 next B;
4355 wakaba 1.55 } elsif ($token->{type} == COMMENT_TOKEN) {
4356 wakaba 1.52 my $comment = $self->{document}->create_comment ($token->{data});
4357 wakaba 1.56 if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4358 wakaba 1.79 !!!cp ('t85');
4359 wakaba 1.52 $self->{document}->append_child ($comment);
4360 wakaba 1.54 } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4361 wakaba 1.79 !!!cp ('t86');
4362 wakaba 1.52 $self->{open_elements}->[0]->[0]->append_child ($comment);
4363     } else {
4364 wakaba 1.79 !!!cp ('t87');
4365 wakaba 1.52 $self->{open_elements}->[-1]->[0]->append_child ($comment);
4366 wakaba 1.202 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4367 wakaba 1.52 }
4368     !!!next-token;
4369 wakaba 1.126 next B;
4370 wakaba 1.205 } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4371     if ($token->{type} == CHARACTER_TOKEN) {
4372     $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4373     delete $self->{ignore_newline};
4374    
4375     if (length $token->{data}) {
4376     !!!cp ('t43');
4377     $self->{open_elements}->[-1]->[0]->manakai_append_text
4378     ($token->{data});
4379     } else {
4380     !!!cp ('t43.1');
4381     }
4382     !!!next-token;
4383     next B;
4384     } elsif ($token->{type} == END_TAG_TOKEN) {
4385     delete $self->{ignore_newline};
4386    
4387     if ($token->{tag_name} eq 'script') {
4388     !!!cp ('t50');
4389    
4390     ## Para 1-2
4391     my $script = pop @{$self->{open_elements}};
4392    
4393     ## Para 3
4394     $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4395    
4396     ## Para 4
4397     ## TODO: $old_insertion_point = $current_insertion_point;
4398     ## TODO: $current_insertion_point = just before $self->{nc};
4399    
4400     ## Para 5
4401     ## TODO: Run the $script->[0].
4402    
4403     ## Para 6
4404     ## TODO: $current_insertion_point = $old_insertion_point;
4405    
4406     ## Para 7
4407     ## TODO: if ($pending_external_script) {
4408     ## TODO: ...
4409     ## TODO: }
4410    
4411     !!!next-token;
4412     next B;
4413     } else {
4414     !!!cp ('t42');
4415    
4416     pop @{$self->{open_elements}};
4417    
4418     $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4419     !!!next-token;
4420     next B;
4421     }
4422     } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4423     delete $self->{ignore_newline};
4424    
4425     !!!cp ('t44');
4426     !!!parse-error (type => 'not closed',
4427     text => $self->{open_elements}->[-1]->[0]
4428     ->manakai_local_name,
4429     token => $token);
4430    
4431     #if ($self->{open_elements}->[-1]->[1] & SCRIPT_EL) {
4432     # ## TODO: Mark as "already executed"
4433     #}
4434    
4435     pop @{$self->{open_elements}};
4436    
4437     $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4438     ## Reprocess.
4439     next B;
4440     } else {
4441     die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";
4442     }
4443 wakaba 1.126 } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4444     if ($token->{type} == CHARACTER_TOKEN) {
4445     !!!cp ('t87.1');
4446     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4447     !!!next-token;
4448     next B;
4449     } elsif ($token->{type} == START_TAG_TOKEN) {
4450 wakaba 1.129 if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4451     $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4452 wakaba 1.126 not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4453     ($token->{tag_name} eq 'svg' and
4454     $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4455     ## NOTE: "using the rules for secondary insertion mode"then"continue"
4456     !!!cp ('t87.2');
4457     #
4458     } elsif ({
4459 wakaba 1.130 b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4460 wakaba 1.146 center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4461     em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4462     h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4463     img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4464     nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4465     small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4466     sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4467 wakaba 1.126 }->{$token->{tag_name}}) {
4468     !!!cp ('t87.2');
4469     !!!parse-error (type => 'not closed',
4470 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
4471 wakaba 1.126 ->manakai_local_name,
4472     token => $token);
4473    
4474     pop @{$self->{open_elements}}
4475     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4476    
4477 wakaba 1.130 $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4478 wakaba 1.126 ## Reprocess.
4479     next B;
4480     } else {
4481 wakaba 1.131 my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4482     my $tag_name = $token->{tag_name};
4483     if ($nsuri eq $SVG_NS) {
4484     $tag_name = {
4485     altglyph => 'altGlyph',
4486     altglyphdef => 'altGlyphDef',
4487     altglyphitem => 'altGlyphItem',
4488     animatecolor => 'animateColor',
4489     animatemotion => 'animateMotion',
4490     animatetransform => 'animateTransform',
4491     clippath => 'clipPath',
4492     feblend => 'feBlend',
4493     fecolormatrix => 'feColorMatrix',
4494     fecomponenttransfer => 'feComponentTransfer',
4495     fecomposite => 'feComposite',
4496     feconvolvematrix => 'feConvolveMatrix',
4497     fediffuselighting => 'feDiffuseLighting',
4498     fedisplacementmap => 'feDisplacementMap',
4499     fedistantlight => 'feDistantLight',
4500     feflood => 'feFlood',
4501     fefunca => 'feFuncA',
4502     fefuncb => 'feFuncB',
4503     fefuncg => 'feFuncG',
4504     fefuncr => 'feFuncR',
4505     fegaussianblur => 'feGaussianBlur',
4506     feimage => 'feImage',
4507     femerge => 'feMerge',
4508     femergenode => 'feMergeNode',
4509     femorphology => 'feMorphology',
4510     feoffset => 'feOffset',
4511     fepointlight => 'fePointLight',
4512     fespecularlighting => 'feSpecularLighting',
4513     fespotlight => 'feSpotLight',
4514     fetile => 'feTile',
4515     feturbulence => 'feTurbulence',
4516     foreignobject => 'foreignObject',
4517     glyphref => 'glyphRef',
4518     lineargradient => 'linearGradient',
4519     radialgradient => 'radialGradient',
4520     #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4521     textpath => 'textPath',
4522     }->{$tag_name} || $tag_name;
4523     }
4524    
4525     ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4526    
4527     ## "adjust foreign attributes" - done in insert-element-f
4528 wakaba 1.126
4529 wakaba 1.131 !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4530 wakaba 1.126
4531     if ($self->{self_closing}) {
4532     pop @{$self->{open_elements}};
4533     !!!ack ('t87.3');
4534     } else {
4535     !!!cp ('t87.4');
4536     }
4537    
4538     !!!next-token;
4539     next B;
4540     }
4541     } elsif ($token->{type} == END_TAG_TOKEN) {
4542     ## NOTE: "using the rules for secondary insertion mode" then "continue"
4543     !!!cp ('t87.5');
4544     #
4545     } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4546     !!!cp ('t87.6');
4547 wakaba 1.146 !!!parse-error (type => 'not closed',
4548 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
4549 wakaba 1.146 ->manakai_local_name,
4550     token => $token);
4551    
4552     pop @{$self->{open_elements}}
4553     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4554    
4555 wakaba 1.200 ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4556    
4557 wakaba 1.146 $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4558     ## Reprocess.
4559     next B;
4560 wakaba 1.126 } else {
4561     die "$0: $token->{type}: Unknown token type";
4562     }
4563     }
4564    
4565     if ($self->{insertion_mode} & HEAD_IMS) {
4566 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
4567 wakaba 1.188 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4568 wakaba 1.99 unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4569 wakaba 1.202 if ($self->{head_element_inserted}) {
4570     !!!cp ('t88.3');
4571     $self->{open_elements}->[-1]->[0]->append_child
4572     ($self->{document}->create_text_node ($1));
4573     delete $self->{head_element_inserted};
4574     ## NOTE: |</head> <link> |
4575     #
4576     } else {
4577     !!!cp ('t88.2');
4578     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4579     ## NOTE: |</head> &#x20;|
4580     #
4581     }
4582 wakaba 1.99 } else {
4583     !!!cp ('t88.1');
4584     ## Ignore the token.
4585 wakaba 1.177 #
4586 wakaba 1.99 }
4587 wakaba 1.52 unless (length $token->{data}) {
4588 wakaba 1.79 !!!cp ('t88');
4589 wakaba 1.52 !!!next-token;
4590 wakaba 1.126 next B;
4591 wakaba 1.1 }
4592 wakaba 1.177 ## TODO: set $token->{column} appropriately
4593 wakaba 1.1 }
4594 wakaba 1.52
4595 wakaba 1.54 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4596 wakaba 1.79 !!!cp ('t89');
4597 wakaba 1.52 ## As if <head>
4598 wakaba 1.126 !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4599 wakaba 1.52 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4600 wakaba 1.123 push @{$self->{open_elements}},
4601     [$self->{head_element}, $el_category->{head}];
4602 wakaba 1.52
4603     ## Reprocess in the "in head" insertion mode...
4604     pop @{$self->{open_elements}};
4605    
4606     ## Reprocess in the "after head" insertion mode...
4607 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4608 wakaba 1.79 !!!cp ('t90');
4609 wakaba 1.52 ## As if </noscript>
4610     pop @{$self->{open_elements}};
4611 wakaba 1.153 !!!parse-error (type => 'in noscript:#text', token => $token);
4612 wakaba 1.1
4613 wakaba 1.52 ## Reprocess in the "in head" insertion mode...
4614     ## As if </head>
4615     pop @{$self->{open_elements}};
4616    
4617     ## Reprocess in the "after head" insertion mode...
4618 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4619 wakaba 1.79 !!!cp ('t91');
4620 wakaba 1.52 pop @{$self->{open_elements}};
4621    
4622     ## Reprocess in the "after head" insertion mode...
4623 wakaba 1.79 } else {
4624     !!!cp ('t92');
4625 wakaba 1.1 }
4626 wakaba 1.52
4627 wakaba 1.123 ## "after head" insertion mode
4628     ## As if <body>
4629     !!!insert-element ('body',, $token);
4630     $self->{insertion_mode} = IN_BODY_IM;
4631     ## reprocess
4632 wakaba 1.126 next B;
4633 wakaba 1.123 } elsif ($token->{type} == START_TAG_TOKEN) {
4634     if ($token->{tag_name} eq 'head') {
4635     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4636     !!!cp ('t93');
4637 wakaba 1.126 !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4638 wakaba 1.123 $self->{open_elements}->[-1]->[0]->append_child
4639     ($self->{head_element});
4640     push @{$self->{open_elements}},
4641     [$self->{head_element}, $el_category->{head}];
4642     $self->{insertion_mode} = IN_HEAD_IM;
4643 wakaba 1.125 !!!nack ('t93.1');
4644 wakaba 1.123 !!!next-token;
4645 wakaba 1.126 next B;
4646 wakaba 1.125 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4647 wakaba 1.139 !!!cp ('t93.2');
4648 wakaba 1.153 !!!parse-error (type => 'after head', text => 'head',
4649     token => $token);
4650 wakaba 1.139 ## Ignore the token
4651     !!!nack ('t93.3');
4652     !!!next-token;
4653     next B;
4654 wakaba 1.125 } else {
4655     !!!cp ('t95');
4656 wakaba 1.153 !!!parse-error (type => 'in head:head',
4657     token => $token); # or in head noscript
4658 wakaba 1.125 ## Ignore the token
4659     !!!nack ('t95.1');
4660     !!!next-token;
4661 wakaba 1.126 next B;
4662 wakaba 1.125 }
4663     } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4664 wakaba 1.126 !!!cp ('t96');
4665     ## As if <head>
4666     !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4667     $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4668     push @{$self->{open_elements}},
4669     [$self->{head_element}, $el_category->{head}];
4670 wakaba 1.52
4671 wakaba 1.126 $self->{insertion_mode} = IN_HEAD_IM;
4672     ## Reprocess in the "in head" insertion mode...
4673     } else {
4674     !!!cp ('t97');
4675     }
4676 wakaba 1.52
4677 wakaba 1.202 if ($token->{tag_name} eq 'base') {
4678     if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4679     !!!cp ('t98');
4680     ## As if </noscript>
4681     pop @{$self->{open_elements}};
4682     !!!parse-error (type => 'in noscript', text => 'base',
4683     token => $token);
4684    
4685     $self->{insertion_mode} = IN_HEAD_IM;
4686     ## Reprocess in the "in head" insertion mode...
4687     } else {
4688     !!!cp ('t99');
4689     }
4690 wakaba 1.49
4691 wakaba 1.202 ## NOTE: There is a "as if in head" code clone.
4692     if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4693     !!!cp ('t100');
4694     !!!parse-error (type => 'after head',
4695     text => $token->{tag_name}, token => $token);
4696     push @{$self->{open_elements}},
4697     [$self->{head_element}, $el_category->{head}];
4698     $self->{head_element_inserted} = 1;
4699     } else {
4700     !!!cp ('t101');
4701     }
4702     !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4703     pop @{$self->{open_elements}};
4704     pop @{$self->{open_elements}} # <head>
4705     if $self->{insertion_mode} == AFTER_HEAD_IM;
4706     !!!nack ('t101.1');
4707     !!!next-token;
4708     next B;
4709 wakaba 1.194 } elsif ($token->{tag_name} eq 'link') {
4710     ## NOTE: There is a "as if in head" code clone.
4711     if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4712     !!!cp ('t102');
4713     !!!parse-error (type => 'after head',
4714     text => $token->{tag_name}, token => $token);
4715     push @{$self->{open_elements}},
4716     [$self->{head_element}, $el_category->{head}];
4717 wakaba 1.202 $self->{head_element_inserted} = 1;
4718 wakaba 1.194 } else {
4719     !!!cp ('t103');
4720     }
4721     !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4722     pop @{$self->{open_elements}};
4723     pop @{$self->{open_elements}} # <head>
4724     if $self->{insertion_mode} == AFTER_HEAD_IM;
4725     !!!ack ('t103.1');
4726     !!!next-token;
4727     next B;
4728     } elsif ($token->{tag_name} eq 'command' or
4729     $token->{tag_name} eq 'eventsource') {
4730     if ($self->{insertion_mode} == IN_HEAD_IM) {
4731     ## NOTE: If the insertion mode at the time of the emission
4732     ## of the token was "before head", $self->{insertion_mode}
4733     ## is already changed to |IN_HEAD_IM|.
4734    
4735     ## NOTE: There is a "as if in head" code clone.
4736     !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4737     pop @{$self->{open_elements}};
4738     pop @{$self->{open_elements}} # <head>
4739     if $self->{insertion_mode} == AFTER_HEAD_IM;
4740     !!!ack ('t103.2');
4741     !!!next-token;
4742     next B;
4743     } else {
4744     ## NOTE: "in head noscript" or "after head" insertion mode
4745     ## - in these cases, these tags are treated as same as
4746     ## normal in-body tags.
4747     !!!cp ('t103.3');
4748     #
4749     }
4750 wakaba 1.202 } elsif ($token->{tag_name} eq 'meta') {
4751     ## NOTE: There is a "as if in head" code clone.
4752     if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4753     !!!cp ('t104');
4754     !!!parse-error (type => 'after head',
4755     text => $token->{tag_name}, token => $token);
4756     push @{$self->{open_elements}},
4757     [$self->{head_element}, $el_category->{head}];
4758     $self->{head_element_inserted} = 1;
4759     } else {
4760     !!!cp ('t105');
4761     }
4762     !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4763     my $meta_el = pop @{$self->{open_elements}};
4764 wakaba 1.34
4765     unless ($self->{confident}) {
4766 wakaba 1.134 if ($token->{attributes}->{charset}) {
4767 wakaba 1.79 !!!cp ('t106');
4768 wakaba 1.134 ## NOTE: Whether the encoding is supported or not is handled
4769     ## in the {change_encoding} callback.
4770 wakaba 1.63 $self->{change_encoding}
4771 wakaba 1.114 ->($self, $token->{attributes}->{charset}->{value},
4772     $token);
4773 wakaba 1.66
4774     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4775     ->set_user_data (manakai_has_reference =>
4776     $token->{attributes}->{charset}
4777     ->{has_reference});
4778 wakaba 1.63 } elsif ($token->{attributes}->{content}) {
4779     if ($token->{attributes}->{content}->{value}
4780 wakaba 1.144 =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4781 wakaba 1.186 [\x09\x0A\x0C\x0D\x20]*=
4782     [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4783     ([^"'\x09\x0A\x0C\x0D\x20]
4784     [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4785 wakaba 1.79 !!!cp ('t107');
4786 wakaba 1.134 ## NOTE: Whether the encoding is supported or not is handled
4787     ## in the {change_encoding} callback.
4788 wakaba 1.63 $self->{change_encoding}
4789 wakaba 1.114 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4790     $token);
4791 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4792     ->set_user_data (manakai_has_reference =>
4793     $token->{attributes}->{content}
4794     ->{has_reference});
4795 wakaba 1.79 } else {
4796     !!!cp ('t108');
4797 wakaba 1.63 }
4798 wakaba 1.34 }
4799 wakaba 1.66 } else {
4800     if ($token->{attributes}->{charset}) {
4801 wakaba 1.79 !!!cp ('t109');
4802 wakaba 1.66 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4803     ->set_user_data (manakai_has_reference =>
4804     $token->{attributes}->{charset}
4805     ->{has_reference});
4806     }
4807 wakaba 1.68 if ($token->{attributes}->{content}) {
4808 wakaba 1.79 !!!cp ('t110');
4809 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4810     ->set_user_data (manakai_has_reference =>
4811     $token->{attributes}->{content}
4812     ->{has_reference});
4813     }
4814 wakaba 1.34 }
4815    
4816 wakaba 1.100 pop @{$self->{open_elements}} # <head>
4817 wakaba 1.54 if $self->{insertion_mode} == AFTER_HEAD_IM;
4818 wakaba 1.125 !!!ack ('t110.1');
4819 wakaba 1.34 !!!next-token;
4820 wakaba 1.126 next B;
4821 wakaba 1.202 } elsif ($token->{tag_name} eq 'title') {
4822     if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4823     !!!cp ('t111');
4824     ## As if </noscript>
4825     pop @{$self->{open_elements}};
4826     !!!parse-error (type => 'in noscript', text => 'title',
4827     token => $token);
4828    
4829     $self->{insertion_mode} = IN_HEAD_IM;
4830     ## Reprocess in the "in head" insertion mode...
4831     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4832     !!!cp ('t112');
4833     !!!parse-error (type => 'after head',
4834     text => $token->{tag_name}, token => $token);
4835     push @{$self->{open_elements}},
4836     [$self->{head_element}, $el_category->{head}];
4837     $self->{head_element_inserted} = 1;
4838     } else {
4839     !!!cp ('t113');
4840     }
4841 wakaba 1.49
4842 wakaba 1.202 ## NOTE: There is a "as if in head" code clone.
4843     $parse_rcdata->(RCDATA_CONTENT_MODEL);
4844 wakaba 1.205 ## ISSUE: A spec bug [Bug 6038]
4845     splice @{$self->{open_elements}}, -2, 1, () # <head>
4846     if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4847 wakaba 1.202 next B;
4848     } elsif ($token->{tag_name} eq 'style' or
4849     $token->{tag_name} eq 'noframes') {
4850     ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4851     ## insertion mode IN_HEAD_IM)
4852     ## NOTE: There is a "as if in head" code clone.
4853     if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4854     !!!cp ('t114');
4855     !!!parse-error (type => 'after head',
4856     text => $token->{tag_name}, token => $token);
4857     push @{$self->{open_elements}},
4858     [$self->{head_element}, $el_category->{head}];
4859     $self->{head_element_inserted} = 1;
4860     } else {
4861     !!!cp ('t115');
4862     }
4863     $parse_rcdata->(CDATA_CONTENT_MODEL);
4864 wakaba 1.205 ## ISSUE: A spec bug [Bug 6038]
4865     splice @{$self->{open_elements}}, -2, 1, () # <head>
4866     if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4867 wakaba 1.202 next B;
4868 wakaba 1.205 } elsif ($token->{tag_name} eq 'noscript') {
4869 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_IM) {
4870 wakaba 1.79 !!!cp ('t116');
4871 wakaba 1.25 ## NOTE: and scripting is disalbed
4872 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4873 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4874 wakaba 1.125 !!!nack ('t116.1');
4875 wakaba 1.1 !!!next-token;
4876 wakaba 1.126 next B;
4877 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4878 wakaba 1.79 !!!cp ('t117');
4879 wakaba 1.153 !!!parse-error (type => 'in noscript', text => 'noscript',
4880     token => $token);
4881 wakaba 1.1 ## Ignore the token
4882 wakaba 1.125 !!!nack ('t117.1');
4883 wakaba 1.41 !!!next-token;
4884 wakaba 1.126 next B;
4885 wakaba 1.1 } else {
4886 wakaba 1.79 !!!cp ('t118');
4887 wakaba 1.25 #
4888 wakaba 1.1 }
4889 wakaba 1.202 } elsif ($token->{tag_name} eq 'script') {
4890     if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4891     !!!cp ('t119');
4892     ## As if </noscript>
4893     pop @{$self->{open_elements}};
4894     !!!parse-error (type => 'in noscript', text => 'script',
4895     token => $token);
4896    
4897     $self->{insertion_mode} = IN_HEAD_IM;
4898     ## Reprocess in the "in head" insertion mode...
4899     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4900     !!!cp ('t120');
4901     !!!parse-error (type => 'after head',
4902     text => $token->{tag_name}, token => $token);
4903     push @{$self->{open_elements}},
4904     [$self->{head_element}, $el_category->{head}];
4905     $self->{head_element_inserted} = 1;
4906     } else {
4907     !!!cp ('t121');
4908     }
4909 wakaba 1.49
4910 wakaba 1.202 ## NOTE: There is a "as if in head" code clone.
4911     $script_start_tag->();
4912 wakaba 1.205 ## ISSUE: A spec bug [Bug 6038]
4913     splice @{$self->{open_elements}}, -2, 1 # <head>
4914     if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4915 wakaba 1.202 next B;
4916     } elsif ($token->{tag_name} eq 'body' or
4917     $token->{tag_name} eq 'frameset') {
4918 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4919 wakaba 1.79 !!!cp ('t122');
4920 wakaba 1.49 ## As if </noscript>
4921     pop @{$self->{open_elements}};
4922 wakaba 1.153 !!!parse-error (type => 'in noscript',
4923     text => $token->{tag_name}, token => $token);
4924 wakaba 1.49
4925     ## Reprocess in the "in head" insertion mode...
4926     ## As if </head>
4927     pop @{$self->{open_elements}};
4928    
4929     ## Reprocess in the "after head" insertion mode...
4930 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4931 wakaba 1.79 !!!cp ('t124');
4932 wakaba 1.49 pop @{$self->{open_elements}};
4933    
4934     ## Reprocess in the "after head" insertion mode...
4935 wakaba 1.79 } else {
4936     !!!cp ('t125');
4937 wakaba 1.49 }
4938    
4939     ## "after head" insertion mode
4940 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4941 wakaba 1.54 if ($token->{tag_name} eq 'body') {
4942 wakaba 1.79 !!!cp ('t126');
4943 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
4944     } elsif ($token->{tag_name} eq 'frameset') {
4945 wakaba 1.79 !!!cp ('t127');
4946 wakaba 1.54 $self->{insertion_mode} = IN_FRAMESET_IM;
4947     } else {
4948     die "$0: tag name: $self->{tag_name}";
4949     }
4950 wakaba 1.125 !!!nack ('t127.1');
4951 wakaba 1.1 !!!next-token;
4952 wakaba 1.126 next B;
4953 wakaba 1.1 } else {
4954 wakaba 1.79 !!!cp ('t128');
4955 wakaba 1.1 #
4956     }
4957 wakaba 1.49
4958 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4959 wakaba 1.79 !!!cp ('t129');
4960 wakaba 1.49 ## As if </noscript>
4961     pop @{$self->{open_elements}};
4962 wakaba 1.153 !!!parse-error (type => 'in noscript:/',
4963     text => $token->{tag_name}, token => $token);
4964 wakaba 1.49
4965     ## Reprocess in the "in head" insertion mode...
4966     ## As if </head>
4967 wakaba 1.25 pop @{$self->{open_elements}};
4968 wakaba 1.49
4969     ## Reprocess in the "after head" insertion mode...
4970 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4971 wakaba 1.79 !!!cp ('t130');
4972 wakaba 1.49 ## As if </head>
4973 wakaba 1.25 pop @{$self->{open_elements}};
4974 wakaba 1.49
4975     ## Reprocess in the "after head" insertion mode...
4976 wakaba 1.79 } else {
4977     !!!cp ('t131');
4978 wakaba 1.49 }
4979    
4980     ## "after head" insertion mode
4981     ## As if <body>
4982 wakaba 1.116 !!!insert-element ('body',, $token);
4983 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
4984 wakaba 1.49 ## reprocess
4985 wakaba 1.125 !!!ack-later;
4986 wakaba 1.126 next B;
4987 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
4988 wakaba 1.49 if ($token->{tag_name} eq 'head') {
4989 wakaba 1.54 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4990 wakaba 1.79 !!!cp ('t132');
4991 wakaba 1.50 ## As if <head>
4992 wakaba 1.126 !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4993 wakaba 1.50 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4994 wakaba 1.123 push @{$self->{open_elements}},
4995     [$self->{head_element}, $el_category->{head}];
4996 wakaba 1.50
4997     ## Reprocess in the "in head" insertion mode...
4998     pop @{$self->{open_elements}};
4999 wakaba 1.54 $self->{insertion_mode} = AFTER_HEAD_IM;
5000 wakaba 1.50 !!!next-token;
5001 wakaba 1.126 next B;
5002 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5003 wakaba 1.79 !!!cp ('t133');
5004 wakaba 1.49 ## As if </noscript>
5005     pop @{$self->{open_elements}};
5006 wakaba 1.153 !!!parse-error (type => 'in noscript:/',
5007     text => 'head', token => $token);
5008 wakaba 1.49
5009     ## Reprocess in the "in head" insertion mode...
5010 wakaba 1.50 pop @{$self->{open_elements}};
5011 wakaba 1.54 $self->{insertion_mode} = AFTER_HEAD_IM;
5012 wakaba 1.50 !!!next-token;
5013 wakaba 1.126 next B;
5014 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5015 wakaba 1.79 !!!cp ('t134');
5016 wakaba 1.49 pop @{$self->{open_elements}};
5017 wakaba 1.54 $self->{insertion_mode} = AFTER_HEAD_IM;
5018 wakaba 1.49 !!!next-token;
5019 wakaba 1.126 next B;
5020 wakaba 1.139 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5021     !!!cp ('t134.1');
5022 wakaba 1.153 !!!parse-error (type => 'unmatched end tag', text => 'head',
5023     token => $token);
5024 wakaba 1.139 ## Ignore the token
5025     !!!next-token;
5026     next B;
5027 wakaba 1.49 } else {
5028 wakaba 1.139 die "$0: $self->{insertion_mode}: Unknown insertion mode";
5029 wakaba 1.49 }
5030     } elsif ($token->{tag_name} eq 'noscript') {
5031 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5032 wakaba 1.79 !!!cp ('t136');
5033 wakaba 1.49 pop @{$self->{open_elements}};
5034 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
5035 wakaba 1.49 !!!next-token;
5036 wakaba 1.126 next B;
5037 wakaba 1.139 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5038     $self->{insertion_mode} == AFTER_HEAD_IM) {
5039 wakaba 1.79 !!!cp ('t137');
5040 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5041     text => 'noscript', token => $token);
5042 wakaba 1.50 ## Ignore the token ## ISSUE: An issue in the spec.
5043     !!!next-token;
5044 wakaba 1.126 next B;
5045 wakaba 1.49 } else {
5046 wakaba 1.79 !!!cp ('t138');
5047 wakaba 1.49 #
5048     }
5049     } elsif ({
5050 wakaba 1.31 body => 1, html => 1,
5051     }->{$token->{tag_name}}) {
5052 wakaba 1.203 ## TODO: This branch is entirely redundant.
5053     if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5054 wakaba 1.139 $self->{insertion_mode} == IN_HEAD_IM or
5055     $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5056 wakaba 1.79 !!!cp ('t140');
5057 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5058     text => $token->{tag_name}, token => $token);
5059 wakaba 1.49 ## Ignore the token
5060     !!!next-token;
5061 wakaba 1.126 next B;
5062 wakaba 1.139 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5063     !!!cp ('t140.1');
5064 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5065     text => $token->{tag_name}, token => $token);
5066 wakaba 1.139 ## Ignore the token
5067     !!!next-token;
5068     next B;
5069 wakaba 1.79 } else {
5070 wakaba 1.139 die "$0: $self->{insertion_mode}: Unknown insertion mode";
5071 wakaba 1.49 }
5072 wakaba 1.139 } elsif ($token->{tag_name} eq 'p') {
5073     !!!cp ('t142');
5074 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5075     text => $token->{tag_name}, token => $token);
5076 wakaba 1.139 ## Ignore the token
5077     !!!next-token;
5078     next B;
5079     } elsif ($token->{tag_name} eq 'br') {
5080 wakaba 1.54 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5081 wakaba 1.139 !!!cp ('t142.2');
5082     ## (before head) as if <head>, (in head) as if </head>
5083 wakaba 1.126 !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5084 wakaba 1.50 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5085 wakaba 1.139 $self->{insertion_mode} = AFTER_HEAD_IM;
5086    
5087     ## Reprocess in the "after head" insertion mode...
5088     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5089     !!!cp ('t143.2');
5090     ## As if </head>
5091     pop @{$self->{open_elements}};
5092     $self->{insertion_mode} = AFTER_HEAD_IM;
5093    
5094     ## Reprocess in the "after head" insertion mode...
5095     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5096     !!!cp ('t143.3');
5097     ## ISSUE: Two parse errors for <head><noscript></br>
5098 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5099     text => 'br', token => $token);
5100 wakaba 1.139 ## As if </noscript>
5101     pop @{$self->{open_elements}};
5102     $self->{insertion_mode} = IN_HEAD_IM;
5103 wakaba 1.50
5104     ## Reprocess in the "in head" insertion mode...
5105 wakaba 1.139 ## As if </head>
5106     pop @{$self->{open_elements}};
5107     $self->{insertion_mode} = AFTER_HEAD_IM;
5108    
5109     ## Reprocess in the "after head" insertion mode...
5110     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5111     !!!cp ('t143.4');
5112     #
5113 wakaba 1.79 } else {
5114 wakaba 1.139 die "$0: $self->{insertion_mode}: Unknown insertion mode";
5115 wakaba 1.50 }
5116    
5117 wakaba 1.139 ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5118 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5119     text => 'br', token => $token);
5120 wakaba 1.139 ## Ignore the token
5121     !!!next-token;
5122     next B;
5123 wakaba 1.25 } else {
5124 wakaba 1.139 !!!cp ('t145');
5125 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5126     text => $token->{tag_name}, token => $token);
5127 wakaba 1.139 ## Ignore the token
5128     !!!next-token;
5129     next B;
5130 wakaba 1.49 }
5131    
5132 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5133 wakaba 1.79 !!!cp ('t146');
5134 wakaba 1.49 ## As if </noscript>
5135     pop @{$self->{open_elements}};
5136 wakaba 1.153 !!!parse-error (type => 'in noscript:/',
5137     text => $token->{tag_name}, token => $token);
5138 wakaba 1.49
5139     ## Reprocess in the "in head" insertion mode...
5140     ## As if </head>
5141     pop @{$self->{open_elements}};
5142    
5143     ## Reprocess in the "after head" insertion mode...
5144 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5145 wakaba 1.79 !!!cp ('t147');
5146 wakaba 1.49 ## As if </head>
5147     pop @{$self->{open_elements}};
5148    
5149     ## Reprocess in the "after head" insertion mode...
5150 wakaba 1.54 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5151 wakaba 1.82 ## ISSUE: This case cannot be reached?
5152 wakaba 1.79 !!!cp ('t148');
5153 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5154     text => $token->{tag_name}, token => $token);
5155 wakaba 1.50 ## Ignore the token ## ISSUE: An issue in the spec.
5156     !!!next-token;
5157 wakaba 1.126 next B;
5158 wakaba 1.79 } else {
5159     !!!cp ('t149');
5160 wakaba 1.1 }
5161    
5162 wakaba 1.49 ## "after head" insertion mode
5163     ## As if <body>
5164 wakaba 1.116 !!!insert-element ('body',, $token);
5165 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
5166 wakaba 1.52 ## reprocess
5167 wakaba 1.126 next B;
5168 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5169     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5170     !!!cp ('t149.1');
5171    
5172     ## NOTE: As if <head>
5173 wakaba 1.126 !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5174 wakaba 1.104 $self->{open_elements}->[-1]->[0]->append_child
5175     ($self->{head_element});
5176 wakaba 1.123 #push @{$self->{open_elements}},
5177     # [$self->{head_element}, $el_category->{head}];
5178 wakaba 1.104 #$self->{insertion_mode} = IN_HEAD_IM;
5179     ## NOTE: Reprocess.
5180    
5181     ## NOTE: As if </head>
5182     #pop @{$self->{open_elements}};
5183     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5184     ## NOTE: Reprocess.
5185    
5186     #
5187     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5188     !!!cp ('t149.2');
5189    
5190     ## NOTE: As if </head>
5191     pop @{$self->{open_elements}};
5192     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5193     ## NOTE: Reprocess.
5194    
5195     #
5196     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5197     !!!cp ('t149.3');
5198    
5199 wakaba 1.113 !!!parse-error (type => 'in noscript:#eof', token => $token);
5200 wakaba 1.104
5201     ## As if </noscript>
5202     pop @{$self->{open_elements}};
5203     #$self->{insertion_mode} = IN_HEAD_IM;
5204     ## NOTE: Reprocess.
5205    
5206     ## NOTE: As if </head>
5207     pop @{$self->{open_elements}};
5208     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5209     ## NOTE: Reprocess.
5210    
5211     #
5212     } else {
5213     !!!cp ('t149.4');
5214     #
5215     }
5216    
5217     ## NOTE: As if <body>
5218 wakaba 1.116 !!!insert-element ('body',, $token);
5219 wakaba 1.104 $self->{insertion_mode} = IN_BODY_IM;
5220     ## NOTE: Reprocess.
5221 wakaba 1.126 next B;
5222 wakaba 1.104 } else {
5223     die "$0: $token->{type}: Unknown token type";
5224     }
5225 wakaba 1.56 } elsif ($self->{insertion_mode} & BODY_IMS) {
5226 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
5227 wakaba 1.79 !!!cp ('t150');
5228 wakaba 1.52 ## NOTE: There is a code clone of "character in body".
5229     $reconstruct_active_formatting_elements->($insert_to_current);
5230    
5231     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5232    
5233     !!!next-token;
5234 wakaba 1.126 next B;
5235 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
5236 wakaba 1.52 if ({
5237     caption => 1, col => 1, colgroup => 1, tbody => 1,
5238     td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5239     }->{$token->{tag_name}}) {
5240 wakaba 1.54 if ($self->{insertion_mode} == IN_CELL_IM) {
5241 wakaba 1.52 ## have an element in table scope
5242 wakaba 1.108 for (reverse 0..$#{$self->{open_elements}}) {
5243 wakaba 1.52 my $node = $self->{open_elements}->[$_];
5244 wakaba 1.123 if ($node->[1] & TABLE_CELL_EL) {
5245 wakaba 1.79 !!!cp ('t151');
5246 wakaba 1.108
5247     ## Close the cell
5248 wakaba 1.125 !!!back-token; # <x>
5249 wakaba 1.122 $token = {type => END_TAG_TOKEN,
5250     tag_name => $node->[0]->manakai_local_name,
5251 wakaba 1.114 line => $token->{line},
5252     column => $token->{column}};
5253 wakaba 1.126 next B;
5254 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5255 wakaba 1.79 !!!cp ('t152');
5256 wakaba 1.108 ## ISSUE: This case can never be reached, maybe.
5257     last;
5258 wakaba 1.52 }
5259 wakaba 1.108 }
5260    
5261     !!!cp ('t153');
5262     !!!parse-error (type => 'start tag not allowed',
5263 wakaba 1.153 text => $token->{tag_name}, token => $token);
5264 wakaba 1.108 ## Ignore the token
5265 wakaba 1.125 !!!nack ('t153.1');
5266 wakaba 1.108 !!!next-token;
5267 wakaba 1.126 next B;
5268 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5269 wakaba 1.153 !!!parse-error (type => 'not closed', text => 'caption',
5270     token => $token);
5271 wakaba 1.52
5272 wakaba 1.108 ## NOTE: As if </caption>.
5273 wakaba 1.52 ## have a table element in table scope
5274     my $i;
5275 wakaba 1.108 INSCOPE: {
5276     for (reverse 0..$#{$self->{open_elements}}) {
5277     my $node = $self->{open_elements}->[$_];
5278 wakaba 1.123 if ($node->[1] & CAPTION_EL) {
5279 wakaba 1.108 !!!cp ('t155');
5280     $i = $_;
5281     last INSCOPE;
5282 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5283 wakaba 1.108 !!!cp ('t156');
5284     last;
5285     }
5286 wakaba 1.52 }
5287 wakaba 1.108
5288     !!!cp ('t157');
5289     !!!parse-error (type => 'start tag not allowed',
5290 wakaba 1.153 text => $token->{tag_name}, token => $token);
5291 wakaba 1.108 ## Ignore the token
5292 wakaba 1.125 !!!nack ('t157.1');
5293 wakaba 1.108 !!!next-token;
5294 wakaba 1.126 next B;
5295 wakaba 1.52 } # INSCOPE
5296    
5297     ## generate implied end tags
5298 wakaba 1.123 while ($self->{open_elements}->[-1]->[1]
5299     & END_TAG_OPTIONAL_EL) {
5300 wakaba 1.79 !!!cp ('t158');
5301 wakaba 1.86 pop @{$self->{open_elements}};
5302 wakaba 1.52 }
5303    
5304 wakaba 1.123 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5305 wakaba 1.79 !!!cp ('t159');
5306 wakaba 1.122 !!!parse-error (type => 'not closed',
5307 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
5308 wakaba 1.122 ->manakai_local_name,
5309     token => $token);
5310 wakaba 1.79 } else {
5311     !!!cp ('t160');
5312 wakaba 1.52 }
5313    
5314     splice @{$self->{open_elements}}, $i;
5315    
5316     $clear_up_to_marker->();
5317    
5318 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
5319 wakaba 1.52
5320     ## reprocess
5321 wakaba 1.125 !!!ack-later;
5322 wakaba 1.126 next B;
5323 wakaba 1.52 } else {
5324 wakaba 1.79 !!!cp ('t161');
5325 wakaba 1.52 #
5326     }
5327     } else {
5328 wakaba 1.79 !!!cp ('t162');
5329 wakaba 1.52 #
5330     }
5331 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
5332 wakaba 1.52 if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5333 wakaba 1.54 if ($self->{insertion_mode} == IN_CELL_IM) {
5334 wakaba 1.43 ## have an element in table scope
5335 wakaba 1.52 my $i;
5336 wakaba 1.43 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5337     my $node = $self->{open_elements}->[$_];
5338 wakaba 1.123 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5339 wakaba 1.79 !!!cp ('t163');
5340 wakaba 1.52 $i = $_;
5341 wakaba 1.43 last INSCOPE;
5342 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5343 wakaba 1.79 !!!cp ('t164');
5344 wakaba 1.43 last INSCOPE;
5345     }
5346     } # INSCOPE
5347 wakaba 1.52 unless (defined $i) {
5348 wakaba 1.79 !!!cp ('t165');
5349 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5350     text => $token->{tag_name},
5351     token => $token);
5352 wakaba 1.43 ## Ignore the token
5353     !!!next-token;
5354 wakaba 1.126 next B;
5355 wakaba 1.43 }
5356    
5357 wakaba 1.52 ## generate implied end tags
5358 wakaba 1.123 while ($self->{open_elements}->[-1]->[1]
5359     & END_TAG_OPTIONAL_EL) {
5360 wakaba 1.79 !!!cp ('t166');
5361 wakaba 1.86 pop @{$self->{open_elements}};
5362 wakaba 1.52 }
5363 wakaba 1.86
5364 wakaba 1.123 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5365     ne $token->{tag_name}) {
5366 wakaba 1.79 !!!cp ('t167');
5367 wakaba 1.122 !!!parse-error (type => 'not closed',
5368 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
5369 wakaba 1.122 ->manakai_local_name,
5370     token => $token);
5371 wakaba 1.79 } else {
5372     !!!cp ('t168');
5373 wakaba 1.52 }
5374    
5375     splice @{$self->{open_elements}}, $i;
5376    
5377     $clear_up_to_marker->();
5378    
5379 wakaba 1.54 $self->{insertion_mode} = IN_ROW_IM;
5380 wakaba 1.52
5381     !!!next-token;
5382 wakaba 1.126 next B;
5383 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5384 wakaba 1.79 !!!cp ('t169');
5385 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5386     text => $token->{tag_name}, token => $token);
5387 wakaba 1.52 ## Ignore the token
5388     !!!next-token;
5389 wakaba 1.126 next B;
5390 wakaba 1.52 } else {
5391 wakaba 1.79 !!!cp ('t170');
5392 wakaba 1.52 #
5393     }
5394     } elsif ($token->{tag_name} eq 'caption') {
5395 wakaba 1.54 if ($self->{insertion_mode} == IN_CAPTION_IM) {
5396 wakaba 1.43 ## have a table element in table scope
5397     my $i;
5398 wakaba 1.108 INSCOPE: {
5399     for (reverse 0..$#{$self->{open_elements}}) {
5400     my $node = $self->{open_elements}->[$_];
5401 wakaba 1.123 if ($node->[1] & CAPTION_EL) {
5402 wakaba 1.108 !!!cp ('t171');
5403     $i = $_;
5404     last INSCOPE;
5405 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5406 wakaba 1.108 !!!cp ('t172');
5407     last;
5408     }
5409 wakaba 1.43 }
5410 wakaba 1.108
5411     !!!cp ('t173');
5412     !!!parse-error (type => 'unmatched end tag',
5413 wakaba 1.153 text => $token->{tag_name}, token => $token);
5414 wakaba 1.108 ## Ignore the token
5415     !!!next-token;
5416 wakaba 1.126 next B;
5417 wakaba 1.43 } # INSCOPE
5418    
5419     ## generate implied end tags
5420 wakaba 1.123 while ($self->{open_elements}->[-1]->[1]
5421     & END_TAG_OPTIONAL_EL) {
5422 wakaba 1.79 !!!cp ('t174');
5423 wakaba 1.86 pop @{$self->{open_elements}};
5424 wakaba 1.43 }
5425 wakaba 1.52
5426 wakaba 1.123 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5427 wakaba 1.79 !!!cp ('t175');
5428 wakaba 1.122 !!!parse-error (type => 'not closed',
5429 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
5430 wakaba 1.122 ->manakai_local_name,
5431     token => $token);
5432 wakaba 1.79 } else {
5433     !!!cp ('t176');
5434 wakaba 1.52 }
5435    
5436     splice @{$self->{open_elements}}, $i;
5437    
5438     $clear_up_to_marker->();
5439    
5440 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
5441 wakaba 1.52
5442     !!!next-token;
5443 wakaba 1.126 next B;
5444 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5445 wakaba 1.79 !!!cp ('t177');
5446 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5447     text => $token->{tag_name}, token => $token);
5448 wakaba 1.52 ## Ignore the token
5449     !!!next-token;
5450 wakaba 1.126 next B;
5451 wakaba 1.52 } else {
5452 wakaba 1.79 !!!cp ('t178');
5453 wakaba 1.52 #
5454     }
5455     } elsif ({
5456     table => 1, tbody => 1, tfoot => 1,
5457     thead => 1, tr => 1,
5458     }->{$token->{tag_name}} and
5459 wakaba 1.54 $self->{insertion_mode} == IN_CELL_IM) {
5460 wakaba 1.52 ## have an element in table scope
5461     my $i;
5462     my $tn;
5463 wakaba 1.108 INSCOPE: {
5464     for (reverse 0..$#{$self->{open_elements}}) {
5465     my $node = $self->{open_elements}->[$_];
5466 wakaba 1.123 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5467 wakaba 1.108 !!!cp ('t179');
5468     $i = $_;
5469    
5470     ## Close the cell
5471 wakaba 1.125 !!!back-token; # </x>
5472 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => $tn,
5473     line => $token->{line},
5474     column => $token->{column}};
5475 wakaba 1.126 next B;
5476 wakaba 1.123 } elsif ($node->[1] & TABLE_CELL_EL) {
5477 wakaba 1.108 !!!cp ('t180');
5478 wakaba 1.123 $tn = $node->[0]->manakai_local_name;
5479 wakaba 1.108 ## NOTE: There is exactly one |td| or |th| element
5480     ## in scope in the stack of open elements by definition.
5481 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5482 wakaba 1.108 ## ISSUE: Can this be reached?
5483     !!!cp ('t181');
5484     last;
5485     }
5486 wakaba 1.52 }
5487 wakaba 1.108
5488 wakaba 1.79 !!!cp ('t182');
5489 wakaba 1.108 !!!parse-error (type => 'unmatched end tag',
5490 wakaba 1.153 text => $token->{tag_name}, token => $token);
5491 wakaba 1.52 ## Ignore the token
5492     !!!next-token;
5493 wakaba 1.126 next B;
5494 wakaba 1.108 } # INSCOPE
5495 wakaba 1.52 } elsif ($token->{tag_name} eq 'table' and
5496 wakaba 1.54 $self->{insertion_mode} == IN_CAPTION_IM) {
5497 wakaba 1.153 !!!parse-error (type => 'not closed', text => 'caption',
5498     token => $token);
5499 wakaba 1.52
5500     ## As if </caption>
5501     ## have a table element in table scope
5502     my $i;
5503     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5504     my $node = $self->{open_elements}->[$_];
5505 wakaba 1.123 if ($node->[1] & CAPTION_EL) {
5506 wakaba 1.79 !!!cp ('t184');
5507 wakaba 1.52 $i = $_;
5508     last INSCOPE;
5509 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5510 wakaba 1.79 !!!cp ('t185');
5511 wakaba 1.52 last INSCOPE;
5512     }
5513     } # INSCOPE
5514     unless (defined $i) {
5515 wakaba 1.79 !!!cp ('t186');
5516 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5517     text => 'caption', token => $token);
5518 wakaba 1.52 ## Ignore the token
5519     !!!next-token;
5520 wakaba 1.126 next B;
5521 wakaba 1.52 }
5522    
5523     ## generate implied end tags
5524 wakaba 1.123 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5525 wakaba 1.79 !!!cp ('t187');
5526 wakaba 1.86 pop @{$self->{open_elements}};
5527 wakaba 1.52 }
5528    
5529 wakaba 1.123 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5530 wakaba 1.79 !!!cp ('t188');
5531 wakaba 1.122 !!!parse-error (type => 'not closed',
5532 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
5533 wakaba 1.122 ->manakai_local_name,
5534     token => $token);
5535 wakaba 1.79 } else {
5536     !!!cp ('t189');
5537 wakaba 1.52 }
5538    
5539     splice @{$self->{open_elements}}, $i;
5540    
5541     $clear_up_to_marker->();
5542    
5543 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
5544 wakaba 1.52
5545     ## reprocess
5546 wakaba 1.126 next B;
5547 wakaba 1.52 } elsif ({
5548     body => 1, col => 1, colgroup => 1, html => 1,
5549     }->{$token->{tag_name}}) {
5550 wakaba 1.56 if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5551 wakaba 1.79 !!!cp ('t190');
5552 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5553     text => $token->{tag_name}, token => $token);
5554 wakaba 1.52 ## Ignore the token
5555     !!!next-token;
5556 wakaba 1.126 next B;
5557 wakaba 1.52 } else {
5558 wakaba 1.79 !!!cp ('t191');
5559 wakaba 1.52 #
5560     }
5561     } elsif ({
5562     tbody => 1, tfoot => 1,
5563     thead => 1, tr => 1,
5564     }->{$token->{tag_name}} and
5565 wakaba 1.54 $self->{insertion_mode} == IN_CAPTION_IM) {
5566 wakaba 1.79 !!!cp ('t192');
5567 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
5568     text => $token->{tag_name}, token => $token);
5569 wakaba 1.52 ## Ignore the token
5570     !!!next-token;
5571 wakaba 1.126 next B;
5572 wakaba 1.52 } else {
5573 wakaba 1.79 !!!cp ('t193');
5574 wakaba 1.52 #
5575     }
5576 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5577     for my $entry (@{$self->{open_elements}}) {
5578 wakaba 1.123 unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5579 wakaba 1.104 !!!cp ('t75');
5580 wakaba 1.113 !!!parse-error (type => 'in body:#eof', token => $token);
5581 wakaba 1.104 last;
5582     }
5583     }
5584    
5585     ## Stop parsing.
5586     last B;
5587 wakaba 1.52 } else {
5588     die "$0: $token->{type}: Unknown token type";
5589     }
5590    
5591     $insert = $insert_to_current;
5592     #
5593 wakaba 1.56 } elsif ($self->{insertion_mode} & TABLE_IMS) {
5594 wakaba 1.58 if ($token->{type} == CHARACTER_TOKEN) {
5595 wakaba 1.95 if (not $open_tables->[-1]->[1] and # tainted
5596 wakaba 1.188 $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5597 wakaba 1.95 $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5598 wakaba 1.52
5599 wakaba 1.95 unless (length $token->{data}) {
5600     !!!cp ('t194');
5601     !!!next-token;
5602 wakaba 1.126 next B;
5603 wakaba 1.95 } else {
5604     !!!cp ('t195');
5605     }
5606     }
5607 wakaba 1.52
5608 wakaba 1.153 !!!parse-error (type => 'in table:#text', token => $token);
5609 wakaba 1.52
5610 wakaba 1.202 ## NOTE: As if in body, but insert into the foster parent element.
5611     $reconstruct_active_formatting_elements->($insert_to_foster);
5612 wakaba 1.52
5613 wakaba 1.202 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5614     # MUST
5615     my $foster_parent_element;
5616     my $next_sibling;
5617     my $prev_sibling;
5618     OE: for (reverse 0..$#{$self->{open_elements}}) {
5619     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5620     my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5621     if (defined $parent and $parent->node_type == 1) {
5622     $foster_parent_element = $parent;
5623     !!!cp ('t196');
5624     $next_sibling = $self->{open_elements}->[$_]->[0];
5625     $prev_sibling = $next_sibling->previous_sibling;
5626     #
5627 wakaba 1.52 } else {
5628 wakaba 1.202 !!!cp ('t197');
5629     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5630     $prev_sibling = $foster_parent_element->last_child;
5631     #
5632 wakaba 1.52 }
5633 wakaba 1.202 last OE;
5634     }
5635     } # OE
5636     $foster_parent_element = $self->{open_elements}->[0]->[0] and
5637     $prev_sibling = $foster_parent_element->last_child
5638     unless defined $foster_parent_element;
5639     undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5640     if (defined $prev_sibling and
5641     $prev_sibling->node_type == 3) {
5642     !!!cp ('t198');
5643     $prev_sibling->manakai_append_text ($token->{data});
5644     } else {
5645     !!!cp ('t199');
5646     $foster_parent_element->insert_before
5647     ($self->{document}->create_text_node ($token->{data}),
5648     $next_sibling);
5649     }
5650 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
5651 wakaba 1.202 $open_tables->[-1]->[2] = 1; # ~node inserted
5652 wakaba 1.95 } else {
5653 wakaba 1.202 ## NOTE: Fragment case or in a foster parent'ed element
5654     ## (e.g. |<table><span>a|). In fragment case, whether the
5655     ## character is appended to existing node or a new node is
5656     ## created is irrelevant, since the foster parent'ed nodes
5657     ## are discarded and fragment parsing does not invoke any
5658     ## script.
5659 wakaba 1.95 !!!cp ('t200');
5660 wakaba 1.202 $self->{open_elements}->[-1]->[0]->manakai_append_text
5661     ($token->{data});
5662 wakaba 1.95 }
5663 wakaba 1.52
5664 wakaba 1.95 !!!next-token;
5665 wakaba 1.126 next B;
5666 wakaba 1.58 } elsif ($token->{type} == START_TAG_TOKEN) {
5667 wakaba 1.153 if ({
5668     tr => ($self->{insertion_mode} != IN_ROW_IM),
5669     th => 1, td => 1,
5670     }->{$token->{tag_name}}) {
5671     if ($self->{insertion_mode} == IN_TABLE_IM) {
5672     ## Clear back to table context
5673     while (not ($self->{open_elements}->[-1]->[1]
5674     & TABLE_SCOPING_EL)) {
5675     !!!cp ('t201');
5676     pop @{$self->{open_elements}};
5677     }
5678    
5679     !!!insert-element ('tbody',, $token);
5680     $self->{insertion_mode} = IN_TABLE_BODY_IM;
5681     ## reprocess in the "in table body" insertion mode...
5682     }
5683    
5684     if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5685     unless ($token->{tag_name} eq 'tr') {
5686     !!!cp ('t202');
5687     !!!parse-error (type => 'missing start tag:tr', token => $token);
5688     }
5689 wakaba 1.43
5690 wakaba 1.153 ## Clear back to table body context
5691     while (not ($self->{open_elements}->[-1]->[1]
5692     & TABLE_ROWS_SCOPING_EL)) {
5693     !!!cp ('t203');
5694     ## ISSUE: Can this case be reached?
5695     pop @{$self->{open_elements}};
5696     }
5697 wakaba 1.43
5698 wakaba 1.202 $self->{insertion_mode} = IN_ROW_IM;
5699     if ($token->{tag_name} eq 'tr') {
5700     !!!cp ('t204');
5701     !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5702     $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5703     !!!nack ('t204');
5704     !!!next-token;
5705     next B;
5706     } else {
5707     !!!cp ('t205');
5708     !!!insert-element ('tr',, $token);
5709     ## reprocess in the "in row" insertion mode
5710     }
5711     } else {
5712     !!!cp ('t206');
5713     }
5714 wakaba 1.52
5715     ## Clear back to table row context
5716 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
5717     & TABLE_ROW_SCOPING_EL)) {
5718 wakaba 1.79 !!!cp ('t207');
5719 wakaba 1.52 pop @{$self->{open_elements}};
5720 wakaba 1.43 }
5721 wakaba 1.52
5722 wakaba 1.202 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5723     $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5724     $self->{insertion_mode} = IN_CELL_IM;
5725 wakaba 1.52
5726 wakaba 1.202 push @$active_formatting_elements, ['#marker', ''];
5727 wakaba 1.52
5728 wakaba 1.202 !!!nack ('t207.1');
5729     !!!next-token;
5730     next B;
5731     } elsif ({
5732     caption => 1, col => 1, colgroup => 1,
5733     tbody => 1, tfoot => 1, thead => 1,
5734     tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5735     }->{$token->{tag_name}}) {
5736     if ($self->{insertion_mode} == IN_ROW_IM) {
5737     ## As if </tr>
5738     ## have an element in table scope
5739     my $i;
5740     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5741     my $node = $self->{open_elements}->[$_];
5742     if ($node->[1] & TABLE_ROW_EL) {
5743     !!!cp ('t208');
5744     $i = $_;
5745     last INSCOPE;
5746     } elsif ($node->[1] & TABLE_SCOPING_EL) {
5747     !!!cp ('t209');
5748     last INSCOPE;
5749     }
5750     } # INSCOPE
5751     unless (defined $i) {
5752     !!!cp ('t210');
5753     ## TODO: This type is wrong.
5754     !!!parse-error (type => 'unmacthed end tag',
5755     text => $token->{tag_name}, token => $token);
5756     ## Ignore the token
5757     !!!nack ('t210.1');
5758 wakaba 1.52 !!!next-token;
5759 wakaba 1.126 next B;
5760 wakaba 1.202 }
5761 wakaba 1.43
5762 wakaba 1.52 ## Clear back to table row context
5763 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
5764     & TABLE_ROW_SCOPING_EL)) {
5765 wakaba 1.79 !!!cp ('t211');
5766 wakaba 1.83 ## ISSUE: Can this case be reached?
5767 wakaba 1.52 pop @{$self->{open_elements}};
5768 wakaba 1.1 }
5769 wakaba 1.43
5770 wakaba 1.52 pop @{$self->{open_elements}}; # tr
5771 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
5772 wakaba 1.52 if ($token->{tag_name} eq 'tr') {
5773 wakaba 1.79 !!!cp ('t212');
5774 wakaba 1.52 ## reprocess
5775 wakaba 1.125 !!!ack-later;
5776 wakaba 1.126 next B;
5777 wakaba 1.52 } else {
5778 wakaba 1.79 !!!cp ('t213');
5779 wakaba 1.52 ## reprocess in the "in table body" insertion mode...
5780     }
5781 wakaba 1.1 }
5782 wakaba 1.52
5783 wakaba 1.54 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5784 wakaba 1.52 ## have an element in table scope
5785 wakaba 1.43 my $i;
5786     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5787     my $node = $self->{open_elements}->[$_];
5788 wakaba 1.123 if ($node->[1] & TABLE_ROW_GROUP_EL) {
5789 wakaba 1.79 !!!cp ('t214');
5790 wakaba 1.43 $i = $_;
5791     last INSCOPE;
5792 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5793 wakaba 1.79 !!!cp ('t215');
5794 wakaba 1.43 last INSCOPE;
5795     }
5796     } # INSCOPE
5797 wakaba 1.52 unless (defined $i) {
5798 wakaba 1.79 !!!cp ('t216');
5799 wakaba 1.153 ## TODO: This erorr type is wrong.
5800     !!!parse-error (type => 'unmatched end tag',
5801     text => $token->{tag_name}, token => $token);
5802 wakaba 1.52 ## Ignore the token
5803 wakaba 1.125 !!!nack ('t216.1');
5804 wakaba 1.52 !!!next-token;
5805 wakaba 1.126 next B;
5806 wakaba 1.43 }
5807 wakaba 1.52
5808     ## Clear back to table body context
5809 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
5810     & TABLE_ROWS_SCOPING_EL)) {
5811 wakaba 1.79 !!!cp ('t217');
5812 wakaba 1.83 ## ISSUE: Can this state be reached?
5813 wakaba 1.52 pop @{$self->{open_elements}};
5814 wakaba 1.43 }
5815    
5816 wakaba 1.52 ## As if <{current node}>
5817     ## have an element in table scope
5818     ## true by definition
5819 wakaba 1.43
5820 wakaba 1.52 ## Clear back to table body context
5821     ## nop by definition
5822 wakaba 1.43
5823 wakaba 1.52 pop @{$self->{open_elements}};
5824 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
5825 wakaba 1.52 ## reprocess in "in table" insertion mode...
5826 wakaba 1.79 } else {
5827     !!!cp ('t218');
5828 wakaba 1.52 }
5829    
5830 wakaba 1.202 if ($token->{tag_name} eq 'col') {
5831     ## Clear back to table context
5832     while (not ($self->{open_elements}->[-1]->[1]
5833     & TABLE_SCOPING_EL)) {
5834     !!!cp ('t219');
5835     ## ISSUE: Can this state be reached?
5836     pop @{$self->{open_elements}};
5837     }
5838    
5839     !!!insert-element ('colgroup',, $token);
5840     $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5841     ## reprocess
5842     $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5843     !!!ack-later;
5844     next B;
5845     } elsif ({
5846     caption => 1,
5847     colgroup => 1,
5848     tbody => 1, tfoot => 1, thead => 1,
5849     }->{$token->{tag_name}}) {
5850     ## Clear back to table context
5851 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
5852     & TABLE_SCOPING_EL)) {
5853 wakaba 1.79 !!!cp ('t220');
5854 wakaba 1.83 ## ISSUE: Can this state be reached?
5855 wakaba 1.52 pop @{$self->{open_elements}};
5856 wakaba 1.1 }
5857 wakaba 1.52
5858 wakaba 1.202 push @$active_formatting_elements, ['#marker', '']
5859     if $token->{tag_name} eq 'caption';
5860 wakaba 1.52
5861 wakaba 1.202 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5862     $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5863     $self->{insertion_mode} = {
5864     caption => IN_CAPTION_IM,
5865     colgroup => IN_COLUMN_GROUP_IM,
5866     tbody => IN_TABLE_BODY_IM,
5867     tfoot => IN_TABLE_BODY_IM,
5868     thead => IN_TABLE_BODY_IM,
5869     }->{$token->{tag_name}};
5870     !!!next-token;
5871     !!!nack ('t220.1');
5872     next B;
5873     } else {
5874     die "$0: in table: <>: $token->{tag_name}";
5875     }
5876 wakaba 1.52 } elsif ($token->{tag_name} eq 'table') {
5877 wakaba 1.122 !!!parse-error (type => 'not closed',
5878 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
5879 wakaba 1.122 ->manakai_local_name,
5880     token => $token);
5881 wakaba 1.1
5882 wakaba 1.52 ## As if </table>
5883 wakaba 1.1 ## have a table element in table scope
5884     my $i;
5885 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5886     my $node = $self->{open_elements}->[$_];
5887 wakaba 1.123 if ($node->[1] & TABLE_EL) {
5888 wakaba 1.79 !!!cp ('t221');
5889 wakaba 1.1 $i = $_;
5890     last INSCOPE;
5891 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5892 wakaba 1.79 !!!cp ('t222');
5893 wakaba 1.1 last INSCOPE;
5894     }
5895     } # INSCOPE
5896     unless (defined $i) {
5897 wakaba 1.79 !!!cp ('t223');
5898 wakaba 1.83 ## TODO: The following is wrong, maybe.
5899 wakaba 1.153 !!!parse-error (type => 'unmatched end tag', text => 'table',
5900     token => $token);
5901 wakaba 1.52 ## Ignore tokens </table><table>
5902 wakaba 1.125 !!!nack ('t223.1');
5903 wakaba 1.1 !!!next-token;
5904 wakaba 1.126 next B;
5905 wakaba 1.1 }
5906    
5907 wakaba 1.151 ## TODO: Followings are removed from the latest spec.
5908 wakaba 1.1 ## generate implied end tags
5909 wakaba 1.123 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5910 wakaba 1.79 !!!cp ('t224');
5911 wakaba 1.86 pop @{$self->{open_elements}};
5912 wakaba 1.1 }
5913    
5914 wakaba 1.123 unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5915 wakaba 1.79 !!!cp ('t225');
5916 wakaba 1.122 ## NOTE: |<table><tr><table>|
5917     !!!parse-error (type => 'not closed',
5918 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
5919 wakaba 1.122 ->manakai_local_name,
5920     token => $token);
5921 wakaba 1.79 } else {
5922     !!!cp ('t226');
5923 wakaba 1.1 }
5924    
5925 wakaba 1.3 splice @{$self->{open_elements}}, $i;
5926 wakaba 1.95 pop @{$open_tables};
5927 wakaba 1.1
5928 wakaba 1.52 $self->_reset_insertion_mode;
5929 wakaba 1.1
5930 wakaba 1.125 ## reprocess
5931     !!!ack-later;
5932 wakaba 1.126 next B;
5933 wakaba 1.100 } elsif ($token->{tag_name} eq 'style') {
5934     if (not $open_tables->[-1]->[1]) { # tainted
5935     !!!cp ('t227.8');
5936     ## NOTE: This is a "as if in head" code clone.
5937     $parse_rcdata->(CDATA_CONTENT_MODEL);
5938 wakaba 1.202 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5939 wakaba 1.126 next B;
5940 wakaba 1.100 } else {
5941     !!!cp ('t227.7');
5942     #
5943     }
5944     } elsif ($token->{tag_name} eq 'script') {
5945     if (not $open_tables->[-1]->[1]) { # tainted
5946     !!!cp ('t227.6');
5947     ## NOTE: This is a "as if in head" code clone.
5948     $script_start_tag->();
5949 wakaba 1.202 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5950 wakaba 1.126 next B;
5951 wakaba 1.100 } else {
5952     !!!cp ('t227.5');
5953     #
5954     }
5955 wakaba 1.98 } elsif ($token->{tag_name} eq 'input') {
5956     if (not $open_tables->[-1]->[1]) { # tainted
5957     if ($token->{attributes}->{type}) { ## TODO: case
5958     my $type = lc $token->{attributes}->{type}->{value};
5959     if ($type eq 'hidden') {
5960     !!!cp ('t227.3');
5961 wakaba 1.153 !!!parse-error (type => 'in table',
5962     text => $token->{tag_name}, token => $token);
5963 wakaba 1.98
5964 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5965 wakaba 1.202 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5966 wakaba 1.98
5967     ## TODO: form element pointer
5968    
5969     pop @{$self->{open_elements}};
5970    
5971     !!!next-token;
5972 wakaba 1.125 !!!ack ('t227.2.1');
5973 wakaba 1.126 next B;
5974 wakaba 1.98 } else {
5975     !!!cp ('t227.2');
5976     #
5977     }
5978     } else {
5979     !!!cp ('t227.1');
5980     #
5981     }
5982     } else {
5983     !!!cp ('t227.4');
5984     #
5985     }
5986 wakaba 1.58 } else {
5987 wakaba 1.79 !!!cp ('t227');
5988 wakaba 1.58 #
5989     }
5990 wakaba 1.98
5991 wakaba 1.153 !!!parse-error (type => 'in table', text => $token->{tag_name},
5992     token => $token);
5993 wakaba 1.98
5994     $insert = $insert_to_foster;
5995     #
5996 wakaba 1.58 } elsif ($token->{type} == END_TAG_TOKEN) {
5997 wakaba 1.52 if ($token->{tag_name} eq 'tr' and
5998 wakaba 1.54 $self->{insertion_mode} == IN_ROW_IM) {
5999 wakaba 1.52 ## have an element in table scope
6000     my $i;
6001     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6002     my $node = $self->{open_elements}->[$_];
6003 wakaba 1.123 if ($node->[1] & TABLE_ROW_EL) {
6004 wakaba 1.79 !!!cp ('t228');
6005 wakaba 1.52 $i = $_;
6006     last INSCOPE;
6007 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6008 wakaba 1.79 !!!cp ('t229');
6009 wakaba 1.52 last INSCOPE;
6010     }
6011     } # INSCOPE
6012     unless (defined $i) {
6013 wakaba 1.79 !!!cp ('t230');
6014 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6015     text => $token->{tag_name}, token => $token);
6016 wakaba 1.52 ## Ignore the token
6017 wakaba 1.125 !!!nack ('t230.1');
6018 wakaba 1.42 !!!next-token;
6019 wakaba 1.126 next B;
6020 wakaba 1.79 } else {
6021     !!!cp ('t232');
6022 wakaba 1.42 }
6023    
6024 wakaba 1.52 ## Clear back to table row context
6025 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
6026     & TABLE_ROW_SCOPING_EL)) {
6027 wakaba 1.79 !!!cp ('t231');
6028 wakaba 1.83 ## ISSUE: Can this state be reached?
6029 wakaba 1.52 pop @{$self->{open_elements}};
6030     }
6031 wakaba 1.42
6032 wakaba 1.52 pop @{$self->{open_elements}}; # tr
6033 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
6034 wakaba 1.52 !!!next-token;
6035 wakaba 1.125 !!!nack ('t231.1');
6036 wakaba 1.126 next B;
6037 wakaba 1.52 } elsif ($token->{tag_name} eq 'table') {
6038 wakaba 1.54 if ($self->{insertion_mode} == IN_ROW_IM) {
6039 wakaba 1.52 ## As if </tr>
6040     ## have an element in table scope
6041     my $i;
6042     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6043     my $node = $self->{open_elements}->[$_];
6044 wakaba 1.123 if ($node->[1] & TABLE_ROW_EL) {
6045 wakaba 1.79 !!!cp ('t233');
6046 wakaba 1.52 $i = $_;
6047     last INSCOPE;
6048 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6049 wakaba 1.79 !!!cp ('t234');
6050 wakaba 1.52 last INSCOPE;
6051 wakaba 1.42 }
6052 wakaba 1.52 } # INSCOPE
6053     unless (defined $i) {
6054 wakaba 1.79 !!!cp ('t235');
6055 wakaba 1.83 ## TODO: The following is wrong.
6056 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6057     text => $token->{type}, token => $token);
6058 wakaba 1.52 ## Ignore the token
6059 wakaba 1.125 !!!nack ('t236.1');
6060 wakaba 1.52 !!!next-token;
6061 wakaba 1.126 next B;
6062 wakaba 1.42 }
6063 wakaba 1.52
6064     ## Clear back to table row context
6065 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
6066     & TABLE_ROW_SCOPING_EL)) {
6067 wakaba 1.79 !!!cp ('t236');
6068 wakaba 1.83 ## ISSUE: Can this state be reached?
6069 wakaba 1.46 pop @{$self->{open_elements}};
6070 wakaba 1.1 }
6071 wakaba 1.46
6072 wakaba 1.52 pop @{$self->{open_elements}}; # tr
6073 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
6074 wakaba 1.46 ## reprocess in the "in table body" insertion mode...
6075 wakaba 1.1 }
6076    
6077 wakaba 1.54 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6078 wakaba 1.52 ## have an element in table scope
6079     my $i;
6080     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6081     my $node = $self->{open_elements}->[$_];
6082 wakaba 1.123 if ($node->[1] & TABLE_ROW_GROUP_EL) {
6083 wakaba 1.79 !!!cp ('t237');
6084 wakaba 1.52 $i = $_;
6085     last INSCOPE;
6086 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6087 wakaba 1.79 !!!cp ('t238');
6088 wakaba 1.52 last INSCOPE;
6089     }
6090     } # INSCOPE
6091     unless (defined $i) {
6092 wakaba 1.79 !!!cp ('t239');
6093 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6094     text => $token->{tag_name}, token => $token);
6095 wakaba 1.52 ## Ignore the token
6096 wakaba 1.125 !!!nack ('t239.1');
6097 wakaba 1.52 !!!next-token;
6098 wakaba 1.126 next B;
6099 wakaba 1.47 }
6100    
6101     ## Clear back to table body context
6102 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
6103     & TABLE_ROWS_SCOPING_EL)) {
6104 wakaba 1.79 !!!cp ('t240');
6105 wakaba 1.47 pop @{$self->{open_elements}};
6106     }
6107    
6108 wakaba 1.52 ## As if <{current node}>
6109     ## have an element in table scope
6110     ## true by definition
6111    
6112     ## Clear back to table body context
6113     ## nop by definition
6114    
6115     pop @{$self->{open_elements}};
6116 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
6117 wakaba 1.52 ## reprocess in the "in table" insertion mode...
6118     }
6119    
6120 wakaba 1.94 ## NOTE: </table> in the "in table" insertion mode.
6121     ## When you edit the code fragment below, please ensure that
6122     ## the code for <table> in the "in table" insertion mode
6123     ## is synced with it.
6124    
6125 wakaba 1.52 ## have a table element in table scope
6126     my $i;
6127     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6128     my $node = $self->{open_elements}->[$_];
6129 wakaba 1.123 if ($node->[1] & TABLE_EL) {
6130 wakaba 1.79 !!!cp ('t241');
6131 wakaba 1.52 $i = $_;
6132     last INSCOPE;
6133 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6134 wakaba 1.79 !!!cp ('t242');
6135 wakaba 1.52 last INSCOPE;
6136 wakaba 1.47 }
6137 wakaba 1.52 } # INSCOPE
6138     unless (defined $i) {
6139 wakaba 1.79 !!!cp ('t243');
6140 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6141     text => $token->{tag_name}, token => $token);
6142 wakaba 1.52 ## Ignore the token
6143 wakaba 1.125 !!!nack ('t243.1');
6144 wakaba 1.52 !!!next-token;
6145 wakaba 1.126 next B;
6146 wakaba 1.3 }
6147 wakaba 1.52
6148     splice @{$self->{open_elements}}, $i;
6149 wakaba 1.95 pop @{$open_tables};
6150 wakaba 1.1
6151 wakaba 1.52 $self->_reset_insertion_mode;
6152 wakaba 1.47
6153     !!!next-token;
6154 wakaba 1.126 next B;
6155 wakaba 1.47 } elsif ({
6156 wakaba 1.48 tbody => 1, tfoot => 1, thead => 1,
6157 wakaba 1.52 }->{$token->{tag_name}} and
6158 wakaba 1.56 $self->{insertion_mode} & ROW_IMS) {
6159 wakaba 1.54 if ($self->{insertion_mode} == IN_ROW_IM) {
6160 wakaba 1.52 ## have an element in table scope
6161     my $i;
6162     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6163     my $node = $self->{open_elements}->[$_];
6164 wakaba 1.123 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6165 wakaba 1.79 !!!cp ('t247');
6166 wakaba 1.52 $i = $_;
6167     last INSCOPE;
6168 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6169 wakaba 1.79 !!!cp ('t248');
6170 wakaba 1.52 last INSCOPE;
6171     }
6172     } # INSCOPE
6173     unless (defined $i) {
6174 wakaba 1.79 !!!cp ('t249');
6175 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6176     text => $token->{tag_name}, token => $token);
6177 wakaba 1.52 ## Ignore the token
6178 wakaba 1.125 !!!nack ('t249.1');
6179 wakaba 1.52 !!!next-token;
6180 wakaba 1.126 next B;
6181 wakaba 1.52 }
6182    
6183 wakaba 1.48 ## As if </tr>
6184     ## have an element in table scope
6185     my $i;
6186     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6187     my $node = $self->{open_elements}->[$_];
6188 wakaba 1.123 if ($node->[1] & TABLE_ROW_EL) {
6189 wakaba 1.79 !!!cp ('t250');
6190 wakaba 1.48 $i = $_;
6191     last INSCOPE;
6192 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6193 wakaba 1.79 !!!cp ('t251');
6194 wakaba 1.48 last INSCOPE;
6195     }
6196     } # INSCOPE
6197 wakaba 1.52 unless (defined $i) {
6198 wakaba 1.79 !!!cp ('t252');
6199 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6200     text => 'tr', token => $token);
6201 wakaba 1.52 ## Ignore the token
6202 wakaba 1.125 !!!nack ('t252.1');
6203 wakaba 1.52 !!!next-token;
6204 wakaba 1.126 next B;
6205 wakaba 1.52 }
6206 wakaba 1.48
6207     ## Clear back to table row context
6208 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
6209     & TABLE_ROW_SCOPING_EL)) {
6210 wakaba 1.79 !!!cp ('t253');
6211 wakaba 1.83 ## ISSUE: Can this case be reached?
6212 wakaba 1.48 pop @{$self->{open_elements}};
6213     }
6214    
6215     pop @{$self->{open_elements}}; # tr
6216 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
6217 wakaba 1.52 ## reprocess in the "in table body" insertion mode...
6218     }
6219    
6220     ## have an element in table scope
6221     my $i;
6222     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6223     my $node = $self->{open_elements}->[$_];
6224 wakaba 1.123 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6225 wakaba 1.79 !!!cp ('t254');
6226 wakaba 1.52 $i = $_;
6227     last INSCOPE;
6228 wakaba 1.123 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6229 wakaba 1.79 !!!cp ('t255');
6230 wakaba 1.52 last INSCOPE;
6231     }
6232     } # INSCOPE
6233     unless (defined $i) {
6234 wakaba 1.79 !!!cp ('t256');
6235 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6236     text => $token->{tag_name}, token => $token);
6237 wakaba 1.52 ## Ignore the token
6238 wakaba 1.125 !!!nack ('t256.1');
6239 wakaba 1.52 !!!next-token;
6240 wakaba 1.126 next B;
6241 wakaba 1.52 }
6242    
6243     ## Clear back to table body context
6244 wakaba 1.123 while (not ($self->{open_elements}->[-1]->[1]
6245     & TABLE_ROWS_SCOPING_EL)) {
6246 wakaba 1.79 !!!cp ('t257');
6247 wakaba 1.83 ## ISSUE: Can this case be reached?
6248 wakaba 1.52 pop @{$self->{open_elements}};
6249     }
6250    
6251     pop @{$self->{open_elements}};
6252 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
6253 wakaba 1.125 !!!nack ('t257.1');
6254 wakaba 1.52 !!!next-token;
6255 wakaba 1.126 next B;
6256 wakaba 1.52 } elsif ({
6257     body => 1, caption => 1, col => 1, colgroup => 1,
6258     html => 1, td => 1, th => 1,
6259 wakaba 1.54 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6260     tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6261 wakaba 1.52 }->{$token->{tag_name}}) {
6262 wakaba 1.125 !!!cp ('t258');
6263 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6264     text => $token->{tag_name}, token => $token);
6265 wakaba 1.125 ## Ignore the token
6266     !!!nack ('t258.1');
6267     !!!next-token;
6268 wakaba 1.126 next B;
6269 wakaba 1.58 } else {
6270 wakaba 1.79 !!!cp ('t259');
6271 wakaba 1.153 !!!parse-error (type => 'in table:/',
6272     text => $token->{tag_name}, token => $token);
6273 wakaba 1.52
6274 wakaba 1.58 $insert = $insert_to_foster;
6275     #
6276     }
6277 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6278 wakaba 1.123 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6279 wakaba 1.104 @{$self->{open_elements}} == 1) { # redundant, maybe
6280 wakaba 1.113 !!!parse-error (type => 'in body:#eof', token => $token);
6281 wakaba 1.104 !!!cp ('t259.1');
6282 wakaba 1.105 #
6283 wakaba 1.104 } else {
6284     !!!cp ('t259.2');
6285 wakaba 1.105 #
6286 wakaba 1.104 }
6287    
6288     ## Stop parsing
6289     last B;
6290 wakaba 1.58 } else {
6291     die "$0: $token->{type}: Unknown token type";
6292     }
6293 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6294 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
6295 wakaba 1.188 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6296 wakaba 1.52 $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6297     unless (length $token->{data}) {
6298 wakaba 1.79 !!!cp ('t260');
6299 wakaba 1.52 !!!next-token;
6300 wakaba 1.126 next B;
6301 wakaba 1.52 }
6302     }
6303    
6304 wakaba 1.79 !!!cp ('t261');
6305 wakaba 1.52 #
6306 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
6307 wakaba 1.52 if ($token->{tag_name} eq 'col') {
6308 wakaba 1.79 !!!cp ('t262');
6309 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6310 wakaba 1.52 pop @{$self->{open_elements}};
6311 wakaba 1.125 !!!ack ('t262.1');
6312 wakaba 1.52 !!!next-token;
6313 wakaba 1.126 next B;
6314 wakaba 1.52 } else {
6315 wakaba 1.79 !!!cp ('t263');
6316 wakaba 1.52 #
6317     }
6318 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
6319 wakaba 1.52 if ($token->{tag_name} eq 'colgroup') {
6320 wakaba 1.123 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6321 wakaba 1.79 !!!cp ('t264');
6322 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6323     text => 'colgroup', token => $token);
6324 wakaba 1.52 ## Ignore the token
6325     !!!next-token;
6326 wakaba 1.126 next B;
6327 wakaba 1.52 } else {
6328 wakaba 1.79 !!!cp ('t265');
6329 wakaba 1.52 pop @{$self->{open_elements}}; # colgroup
6330 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
6331 wakaba 1.52 !!!next-token;
6332 wakaba 1.126 next B;
6333 wakaba 1.52 }
6334     } elsif ($token->{tag_name} eq 'col') {
6335 wakaba 1.79 !!!cp ('t266');
6336 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6337     text => 'col', token => $token);
6338 wakaba 1.52 ## Ignore the token
6339     !!!next-token;
6340 wakaba 1.126 next B;
6341 wakaba 1.52 } else {
6342 wakaba 1.79 !!!cp ('t267');
6343 wakaba 1.52 #
6344     }
6345 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6346 wakaba 1.123 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6347 wakaba 1.104 @{$self->{open_elements}} == 1) { # redundant, maybe
6348     !!!cp ('t270.2');
6349     ## Stop parsing.
6350     last B;
6351     } else {
6352     ## NOTE: As if </colgroup>.
6353     !!!cp ('t270.1');
6354     pop @{$self->{open_elements}}; # colgroup
6355     $self->{insertion_mode} = IN_TABLE_IM;
6356     ## Reprocess.
6357 wakaba 1.126 next B;
6358 wakaba 1.104 }
6359     } else {
6360     die "$0: $token->{type}: Unknown token type";
6361     }
6362 wakaba 1.52
6363     ## As if </colgroup>
6364 wakaba 1.123 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6365 wakaba 1.79 !!!cp ('t269');
6366 wakaba 1.104 ## TODO: Wrong error type?
6367 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6368     text => 'colgroup', token => $token);
6369 wakaba 1.52 ## Ignore the token
6370 wakaba 1.125 !!!nack ('t269.1');
6371 wakaba 1.52 !!!next-token;
6372 wakaba 1.126 next B;
6373 wakaba 1.52 } else {
6374 wakaba 1.79 !!!cp ('t270');
6375 wakaba 1.52 pop @{$self->{open_elements}}; # colgroup
6376 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
6377 wakaba 1.125 !!!ack-later;
6378 wakaba 1.52 ## reprocess
6379 wakaba 1.126 next B;
6380 wakaba 1.52 }
6381 wakaba 1.101 } elsif ($self->{insertion_mode} & SELECT_IMS) {
6382 wakaba 1.58 if ($token->{type} == CHARACTER_TOKEN) {
6383 wakaba 1.79 !!!cp ('t271');
6384 wakaba 1.58 $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6385     !!!next-token;
6386 wakaba 1.126 next B;
6387 wakaba 1.58 } elsif ($token->{type} == START_TAG_TOKEN) {
6388 wakaba 1.123 if ($token->{tag_name} eq 'option') {
6389     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6390     !!!cp ('t272');
6391     ## As if </option>
6392     pop @{$self->{open_elements}};
6393     } else {
6394     !!!cp ('t273');
6395     }
6396 wakaba 1.52
6397 wakaba 1.123 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6398 wakaba 1.125 !!!nack ('t273.1');
6399 wakaba 1.123 !!!next-token;
6400 wakaba 1.126 next B;
6401 wakaba 1.123 } elsif ($token->{tag_name} eq 'optgroup') {
6402     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6403     !!!cp ('t274');
6404     ## As if </option>
6405     pop @{$self->{open_elements}};
6406     } else {
6407     !!!cp ('t275');
6408     }
6409 wakaba 1.52
6410 wakaba 1.123 if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6411     !!!cp ('t276');
6412     ## As if </optgroup>
6413     pop @{$self->{open_elements}};
6414     } else {
6415     !!!cp ('t277');
6416     }
6417 wakaba 1.52
6418 wakaba 1.123 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6419 wakaba 1.125 !!!nack ('t277.1');
6420 wakaba 1.123 !!!next-token;
6421 wakaba 1.126 next B;
6422 wakaba 1.146 } elsif ({
6423     select => 1, input => 1, textarea => 1,
6424     }->{$token->{tag_name}} or
6425 wakaba 1.101 ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6426     {
6427     caption => 1, table => 1,
6428     tbody => 1, tfoot => 1, thead => 1,
6429     tr => 1, td => 1, th => 1,
6430     }->{$token->{tag_name}})) {
6431     ## TODO: The type below is not good - <select> is replaced by </select>
6432 wakaba 1.153 !!!parse-error (type => 'not closed', text => 'select',
6433     token => $token);
6434 wakaba 1.101 ## NOTE: As if the token were </select> (<select> case) or
6435     ## as if there were </select> (otherwise).
6436 wakaba 1.123 ## have an element in table scope
6437     my $i;
6438     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6439     my $node = $self->{open_elements}->[$_];
6440     if ($node->[1] & SELECT_EL) {
6441     !!!cp ('t278');
6442     $i = $_;
6443     last INSCOPE;
6444     } elsif ($node->[1] & TABLE_SCOPING_EL) {
6445     !!!cp ('t279');
6446     last INSCOPE;
6447     }
6448     } # INSCOPE
6449     unless (defined $i) {
6450     !!!cp ('t280');
6451 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6452     text => 'select', token => $token);
6453 wakaba 1.123 ## Ignore the token
6454 wakaba 1.125 !!!nack ('t280.1');
6455 wakaba 1.123 !!!next-token;
6456 wakaba 1.126 next B;
6457 wakaba 1.123 }
6458 wakaba 1.52
6459 wakaba 1.123 !!!cp ('t281');
6460     splice @{$self->{open_elements}}, $i;
6461 wakaba 1.52
6462 wakaba 1.123 $self->_reset_insertion_mode;
6463 wakaba 1.47
6464 wakaba 1.101 if ($token->{tag_name} eq 'select') {
6465 wakaba 1.125 !!!nack ('t281.2');
6466 wakaba 1.101 !!!next-token;
6467 wakaba 1.126 next B;
6468 wakaba 1.101 } else {
6469     !!!cp ('t281.1');
6470 wakaba 1.125 !!!ack-later;
6471 wakaba 1.101 ## Reprocess the token.
6472 wakaba 1.126 next B;
6473 wakaba 1.101 }
6474 wakaba 1.58 } else {
6475 wakaba 1.79 !!!cp ('t282');
6476 wakaba 1.153 !!!parse-error (type => 'in select',
6477     text => $token->{tag_name}, token => $token);
6478 wakaba 1.58 ## Ignore the token
6479 wakaba 1.125 !!!nack ('t282.1');
6480 wakaba 1.58 !!!next-token;
6481 wakaba 1.126 next B;
6482 wakaba 1.58 }
6483     } elsif ($token->{type} == END_TAG_TOKEN) {
6484 wakaba 1.123 if ($token->{tag_name} eq 'optgroup') {
6485     if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6486     $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6487     !!!cp ('t283');
6488     ## As if </option>
6489     splice @{$self->{open_elements}}, -2;
6490     } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6491     !!!cp ('t284');
6492     pop @{$self->{open_elements}};
6493     } else {
6494     !!!cp ('t285');
6495 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6496     text => $token->{tag_name}, token => $token);
6497 wakaba 1.123 ## Ignore the token
6498     }
6499 wakaba 1.125 !!!nack ('t285.1');
6500 wakaba 1.123 !!!next-token;
6501 wakaba 1.126 next B;
6502 wakaba 1.123 } elsif ($token->{tag_name} eq 'option') {
6503     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6504     !!!cp ('t286');
6505     pop @{$self->{open_elements}};
6506     } else {
6507     !!!cp ('t287');
6508 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6509     text => $token->{tag_name}, token => $token);
6510 wakaba 1.123 ## Ignore the token
6511     }
6512 wakaba 1.125 !!!nack ('t287.1');
6513 wakaba 1.123 !!!next-token;
6514 wakaba 1.126 next B;
6515 wakaba 1.123 } elsif ($token->{tag_name} eq 'select') {
6516     ## have an element in table scope
6517     my $i;
6518     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6519     my $node = $self->{open_elements}->[$_];
6520     if ($node->[1] & SELECT_EL) {
6521     !!!cp ('t288');
6522     $i = $_;
6523     last INSCOPE;
6524     } elsif ($node->[1] & TABLE_SCOPING_EL) {
6525     !!!cp ('t289');
6526     last INSCOPE;
6527     }
6528     } # INSCOPE
6529     unless (defined $i) {
6530     !!!cp ('t290');
6531 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6532     text => $token->{tag_name}, token => $token);
6533 wakaba 1.123 ## Ignore the token
6534 wakaba 1.125 !!!nack ('t290.1');
6535 wakaba 1.123 !!!next-token;
6536 wakaba 1.126 next B;
6537 wakaba 1.123 }
6538 wakaba 1.52
6539 wakaba 1.123 !!!cp ('t291');
6540     splice @{$self->{open_elements}}, $i;
6541 wakaba 1.52
6542 wakaba 1.123 $self->_reset_insertion_mode;
6543 wakaba 1.52
6544 wakaba 1.125 !!!nack ('t291.1');
6545 wakaba 1.123 !!!next-token;
6546 wakaba 1.126 next B;
6547 wakaba 1.101 } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6548     {
6549     caption => 1, table => 1, tbody => 1,
6550     tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6551     }->{$token->{tag_name}}) {
6552 wakaba 1.83 ## TODO: The following is wrong?
6553 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6554     text => $token->{tag_name}, token => $token);
6555 wakaba 1.52
6556 wakaba 1.123 ## have an element in table scope
6557     my $i;
6558     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6559     my $node = $self->{open_elements}->[$_];
6560     if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6561     !!!cp ('t292');
6562     $i = $_;
6563     last INSCOPE;
6564     } elsif ($node->[1] & TABLE_SCOPING_EL) {
6565     !!!cp ('t293');
6566     last INSCOPE;
6567     }
6568     } # INSCOPE
6569     unless (defined $i) {
6570     !!!cp ('t294');
6571     ## Ignore the token
6572 wakaba 1.125 !!!nack ('t294.1');
6573 wakaba 1.123 !!!next-token;
6574 wakaba 1.126 next B;
6575 wakaba 1.123 }
6576 wakaba 1.52
6577 wakaba 1.123 ## As if </select>
6578     ## have an element in table scope
6579     undef $i;
6580     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6581     my $node = $self->{open_elements}->[$_];
6582     if ($node->[1] & SELECT_EL) {
6583     !!!cp ('t295');
6584     $i = $_;
6585     last INSCOPE;
6586     } elsif ($node->[1] & TABLE_SCOPING_EL) {
6587 wakaba 1.83 ## ISSUE: Can this state be reached?
6588 wakaba 1.123 !!!cp ('t296');
6589     last INSCOPE;
6590     }
6591     } # INSCOPE
6592     unless (defined $i) {
6593     !!!cp ('t297');
6594 wakaba 1.83 ## TODO: The following error type is correct?
6595 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6596     text => 'select', token => $token);
6597 wakaba 1.123 ## Ignore the </select> token
6598 wakaba 1.125 !!!nack ('t297.1');
6599 wakaba 1.123 !!!next-token; ## TODO: ok?
6600 wakaba 1.126 next B;
6601 wakaba 1.123 }
6602 wakaba 1.52
6603 wakaba 1.123 !!!cp ('t298');
6604     splice @{$self->{open_elements}}, $i;
6605 wakaba 1.52
6606 wakaba 1.123 $self->_reset_insertion_mode;
6607 wakaba 1.52
6608 wakaba 1.125 !!!ack-later;
6609 wakaba 1.123 ## reprocess
6610 wakaba 1.126 next B;
6611 wakaba 1.58 } else {
6612 wakaba 1.79 !!!cp ('t299');
6613 wakaba 1.153 !!!parse-error (type => 'in select:/',
6614     text => $token->{tag_name}, token => $token);
6615 wakaba 1.52 ## Ignore the token
6616 wakaba 1.125 !!!nack ('t299.3');
6617 wakaba 1.52 !!!next-token;
6618 wakaba 1.126 next B;
6619 wakaba 1.58 }
6620 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6621 wakaba 1.123 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6622 wakaba 1.104 @{$self->{open_elements}} == 1) { # redundant, maybe
6623     !!!cp ('t299.1');
6624 wakaba 1.113 !!!parse-error (type => 'in body:#eof', token => $token);
6625 wakaba 1.104 } else {
6626     !!!cp ('t299.2');
6627     }
6628    
6629     ## Stop parsing.
6630     last B;
6631 wakaba 1.58 } else {
6632     die "$0: $token->{type}: Unknown token type";
6633     }
6634 wakaba 1.56 } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6635 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
6636 wakaba 1.188 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6637 wakaba 1.52 my $data = $1;
6638     ## As if in body
6639     $reconstruct_active_formatting_elements->($insert_to_current);
6640    
6641     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6642    
6643     unless (length $token->{data}) {
6644 wakaba 1.79 !!!cp ('t300');
6645 wakaba 1.52 !!!next-token;
6646 wakaba 1.126 next B;
6647 wakaba 1.52 }
6648     }
6649    
6650 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6651 wakaba 1.79 !!!cp ('t301');
6652 wakaba 1.153 !!!parse-error (type => 'after html:#text', token => $token);
6653 wakaba 1.188 #
6654 wakaba 1.79 } else {
6655     !!!cp ('t302');
6656 wakaba 1.188 ## "after body" insertion mode
6657     !!!parse-error (type => 'after body:#text', token => $token);
6658     #
6659 wakaba 1.52 }
6660    
6661 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
6662 wakaba 1.52 ## reprocess
6663 wakaba 1.126 next B;
6664 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
6665 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6666 wakaba 1.79 !!!cp ('t303');
6667 wakaba 1.153 !!!parse-error (type => 'after html',
6668     text => $token->{tag_name}, token => $token);
6669 wakaba 1.188 #
6670 wakaba 1.79 } else {
6671     !!!cp ('t304');
6672 wakaba 1.188 ## "after body" insertion mode
6673     !!!parse-error (type => 'after body',
6674     text => $token->{tag_name}, token => $token);
6675     #
6676 wakaba 1.52 }
6677    
6678 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
6679 wakaba 1.125 !!!ack-later;
6680 wakaba 1.52 ## reprocess
6681 wakaba 1.126 next B;
6682 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
6683 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6684 wakaba 1.79 !!!cp ('t305');
6685 wakaba 1.153 !!!parse-error (type => 'after html:/',
6686     text => $token->{tag_name}, token => $token);
6687 wakaba 1.52
6688 wakaba 1.188 $self->{insertion_mode} = IN_BODY_IM;
6689     ## Reprocess.
6690     next B;
6691 wakaba 1.79 } else {
6692     !!!cp ('t306');
6693 wakaba 1.52 }
6694    
6695     ## "after body" insertion mode
6696     if ($token->{tag_name} eq 'html') {
6697     if (defined $self->{inner_html_node}) {
6698 wakaba 1.79 !!!cp ('t307');
6699 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6700     text => 'html', token => $token);
6701 wakaba 1.52 ## Ignore the token
6702     !!!next-token;
6703 wakaba 1.126 next B;
6704 wakaba 1.52 } else {
6705 wakaba 1.79 !!!cp ('t308');
6706 wakaba 1.54 $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6707 wakaba 1.52 !!!next-token;
6708 wakaba 1.126 next B;
6709 wakaba 1.52 }
6710     } else {
6711 wakaba 1.79 !!!cp ('t309');
6712 wakaba 1.153 !!!parse-error (type => 'after body:/',
6713     text => $token->{tag_name}, token => $token);
6714 wakaba 1.52
6715 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
6716 wakaba 1.52 ## reprocess
6717 wakaba 1.126 next B;
6718 wakaba 1.52 }
6719 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6720     !!!cp ('t309.2');
6721     ## Stop parsing
6722     last B;
6723 wakaba 1.52 } else {
6724     die "$0: $token->{type}: Unknown token type";
6725     }
6726 wakaba 1.56 } elsif ($self->{insertion_mode} & FRAME_IMS) {
6727 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
6728 wakaba 1.188 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6729 wakaba 1.52 $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6730    
6731     unless (length $token->{data}) {
6732 wakaba 1.79 !!!cp ('t310');
6733 wakaba 1.52 !!!next-token;
6734 wakaba 1.126 next B;
6735 wakaba 1.52 }
6736     }
6737    
6738 wakaba 1.188 if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6739 wakaba 1.54 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6740 wakaba 1.79 !!!cp ('t311');
6741 wakaba 1.153 !!!parse-error (type => 'in frameset:#text', token => $token);
6742 wakaba 1.54 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6743 wakaba 1.79 !!!cp ('t312');
6744 wakaba 1.153 !!!parse-error (type => 'after frameset:#text', token => $token);
6745 wakaba 1.158 } else { # "after after frameset"
6746 wakaba 1.79 !!!cp ('t313');
6747 wakaba 1.153 !!!parse-error (type => 'after html:#text', token => $token);
6748 wakaba 1.52 }
6749    
6750     ## Ignore the token.
6751     if (length $token->{data}) {
6752 wakaba 1.79 !!!cp ('t314');
6753 wakaba 1.52 ## reprocess the rest of characters
6754     } else {
6755 wakaba 1.79 !!!cp ('t315');
6756 wakaba 1.52 !!!next-token;
6757     }
6758 wakaba 1.126 next B;
6759 wakaba 1.52 }
6760    
6761     die qq[$0: Character "$token->{data}"];
6762 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
6763 wakaba 1.52 if ($token->{tag_name} eq 'frameset' and
6764 wakaba 1.54 $self->{insertion_mode} == IN_FRAMESET_IM) {
6765 wakaba 1.79 !!!cp ('t318');
6766 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6767 wakaba 1.125 !!!nack ('t318.1');
6768 wakaba 1.52 !!!next-token;
6769 wakaba 1.126 next B;
6770 wakaba 1.52 } elsif ($token->{tag_name} eq 'frame' and
6771 wakaba 1.54 $self->{insertion_mode} == IN_FRAMESET_IM) {
6772 wakaba 1.79 !!!cp ('t319');
6773 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6774 wakaba 1.52 pop @{$self->{open_elements}};
6775 wakaba 1.125 !!!ack ('t319.1');
6776 wakaba 1.52 !!!next-token;
6777 wakaba 1.126 next B;
6778 wakaba 1.52 } elsif ($token->{tag_name} eq 'noframes') {
6779 wakaba 1.79 !!!cp ('t320');
6780 wakaba 1.148 ## NOTE: As if in head.
6781 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
6782 wakaba 1.126 next B;
6783 wakaba 1.158
6784     ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6785     ## has no parse error.
6786 wakaba 1.52 } else {
6787 wakaba 1.54 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6788 wakaba 1.79 !!!cp ('t321');
6789 wakaba 1.153 !!!parse-error (type => 'in frameset',
6790     text => $token->{tag_name}, token => $token);
6791 wakaba 1.158 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6792 wakaba 1.79 !!!cp ('t322');
6793 wakaba 1.153 !!!parse-error (type => 'after frameset',
6794     text => $token->{tag_name}, token => $token);
6795 wakaba 1.158 } else { # "after after frameset"
6796     !!!cp ('t322.2');
6797     !!!parse-error (type => 'after after frameset',
6798     text => $token->{tag_name}, token => $token);
6799 wakaba 1.52 }
6800     ## Ignore the token
6801 wakaba 1.125 !!!nack ('t322.1');
6802 wakaba 1.52 !!!next-token;
6803 wakaba 1.126 next B;
6804 wakaba 1.52 }
6805 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
6806 wakaba 1.52 if ($token->{tag_name} eq 'frameset' and
6807 wakaba 1.54 $self->{insertion_mode} == IN_FRAMESET_IM) {
6808 wakaba 1.123 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6809 wakaba 1.52 @{$self->{open_elements}} == 1) {
6810 wakaba 1.79 !!!cp ('t325');
6811 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
6812     text => $token->{tag_name}, token => $token);
6813 wakaba 1.52 ## Ignore the token
6814     !!!next-token;
6815     } else {
6816 wakaba 1.79 !!!cp ('t326');
6817 wakaba 1.52 pop @{$self->{open_elements}};
6818     !!!next-token;
6819     }
6820 wakaba 1.47
6821 wakaba 1.52 if (not defined $self->{inner_html_node} and
6822 wakaba 1.123 not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6823 wakaba 1.79 !!!cp ('t327');
6824 wakaba 1.54 $self->{insertion_mode} = AFTER_FRAMESET_IM;
6825 wakaba 1.79 } else {
6826     !!!cp ('t328');
6827 wakaba 1.52 }
6828 wakaba 1.126 next B;
6829 wakaba 1.52 } elsif ($token->{tag_name} eq 'html' and
6830 wakaba 1.54 $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6831 wakaba 1.79 !!!cp ('t329');
6832 wakaba 1.54 $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6833 wakaba 1.52 !!!next-token;
6834 wakaba 1.126 next B;
6835 wakaba 1.52 } else {
6836 wakaba 1.54 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6837 wakaba 1.79 !!!cp ('t330');
6838 wakaba 1.153 !!!parse-error (type => 'in frameset:/',
6839     text => $token->{tag_name}, token => $token);
6840 wakaba 1.158 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6841     !!!cp ('t330.1');
6842     !!!parse-error (type => 'after frameset:/',
6843     text => $token->{tag_name}, token => $token);
6844     } else { # "after after html"
6845 wakaba 1.79 !!!cp ('t331');
6846 wakaba 1.158 !!!parse-error (type => 'after after frameset:/',
6847 wakaba 1.153 text => $token->{tag_name}, token => $token);
6848 wakaba 1.52 }
6849     ## Ignore the token
6850     !!!next-token;
6851 wakaba 1.126 next B;
6852 wakaba 1.52 }
6853 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6854 wakaba 1.123 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6855 wakaba 1.104 @{$self->{open_elements}} == 1) { # redundant, maybe
6856     !!!cp ('t331.1');
6857 wakaba 1.113 !!!parse-error (type => 'in body:#eof', token => $token);
6858 wakaba 1.104 } else {
6859     !!!cp ('t331.2');
6860     }
6861    
6862     ## Stop parsing
6863     last B;
6864 wakaba 1.52 } else {
6865     die "$0: $token->{type}: Unknown token type";
6866     }
6867     } else {
6868     die "$0: $self->{insertion_mode}: Unknown insertion mode";
6869     }
6870 wakaba 1.47
6871 wakaba 1.52 ## "in body" insertion mode
6872 wakaba 1.55 if ($token->{type} == START_TAG_TOKEN) {
6873 wakaba 1.52 if ($token->{tag_name} eq 'script') {
6874 wakaba 1.79 !!!cp ('t332');
6875 wakaba 1.52 ## NOTE: This is an "as if in head" code clone
6876 wakaba 1.100 $script_start_tag->();
6877 wakaba 1.126 next B;
6878 wakaba 1.52 } elsif ($token->{tag_name} eq 'style') {
6879 wakaba 1.79 !!!cp ('t333');
6880 wakaba 1.52 ## NOTE: This is an "as if in head" code clone
6881 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
6882 wakaba 1.126 next B;
6883 wakaba 1.52 } elsif ({
6884 wakaba 1.194 base => 1, command => 1, eventsource => 1, link => 1,
6885 wakaba 1.52 }->{$token->{tag_name}}) {
6886 wakaba 1.79 !!!cp ('t334');
6887 wakaba 1.52 ## NOTE: This is an "as if in head" code clone, only "-t" differs
6888 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6889 wakaba 1.194 pop @{$self->{open_elements}};
6890 wakaba 1.125 !!!ack ('t334.1');
6891 wakaba 1.52 !!!next-token;
6892 wakaba 1.126 next B;
6893 wakaba 1.52 } elsif ($token->{tag_name} eq 'meta') {
6894     ## NOTE: This is an "as if in head" code clone, only "-t" differs
6895 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6896 wakaba 1.194 my $meta_el = pop @{$self->{open_elements}};
6897 wakaba 1.46
6898 wakaba 1.52 unless ($self->{confident}) {
6899 wakaba 1.134 if ($token->{attributes}->{charset}) {
6900 wakaba 1.79 !!!cp ('t335');
6901 wakaba 1.134 ## NOTE: Whether the encoding is supported or not is handled
6902     ## in the {change_encoding} callback.
6903 wakaba 1.63 $self->{change_encoding}
6904 wakaba 1.114 ->($self, $token->{attributes}->{charset}->{value}, $token);
6905 wakaba 1.66
6906     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6907     ->set_user_data (manakai_has_reference =>
6908     $token->{attributes}->{charset}
6909     ->{has_reference});
6910 wakaba 1.63 } elsif ($token->{attributes}->{content}) {
6911     if ($token->{attributes}->{content}->{value}
6912 wakaba 1.144 =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6913 wakaba 1.189 [\x09\x0A\x0C\x0D\x20]*=
6914     [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6915     ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6916     /x) {
6917 wakaba 1.79 !!!cp ('t336');
6918 wakaba 1.134 ## NOTE: Whether the encoding is supported or not is handled
6919     ## in the {change_encoding} callback.
6920 wakaba 1.63 $self->{change_encoding}
6921 wakaba 1.114 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6922 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6923     ->set_user_data (manakai_has_reference =>
6924     $token->{attributes}->{content}
6925     ->{has_reference});
6926 wakaba 1.63 }
6927 wakaba 1.52 }
6928 wakaba 1.66 } else {
6929     if ($token->{attributes}->{charset}) {
6930 wakaba 1.79 !!!cp ('t337');
6931 wakaba 1.66 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6932     ->set_user_data (manakai_has_reference =>
6933     $token->{attributes}->{charset}
6934     ->{has_reference});
6935     }
6936 wakaba 1.68 if ($token->{attributes}->{content}) {
6937 wakaba 1.79 !!!cp ('t338');
6938 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6939     ->set_user_data (manakai_has_reference =>
6940     $token->{attributes}->{content}
6941     ->{has_reference});
6942     }
6943 wakaba 1.52 }
6944 wakaba 1.1
6945 wakaba 1.125 !!!ack ('t338.1');
6946 wakaba 1.52 !!!next-token;
6947 wakaba 1.126 next B;
6948 wakaba 1.52 } elsif ($token->{tag_name} eq 'title') {
6949 wakaba 1.79 !!!cp ('t341');
6950 wakaba 1.52 ## NOTE: This is an "as if in head" code clone
6951 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
6952 wakaba 1.126 next B;
6953 wakaba 1.52 } elsif ($token->{tag_name} eq 'body') {
6954 wakaba 1.153 !!!parse-error (type => 'in body', text => 'body', token => $token);
6955 wakaba 1.46
6956 wakaba 1.52 if (@{$self->{open_elements}} == 1 or
6957 wakaba 1.123 not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6958 wakaba 1.79 !!!cp ('t342');
6959 wakaba 1.52 ## Ignore the token
6960     } else {
6961     my $body_el = $self->{open_elements}->[1]->[0];
6962     for my $attr_name (keys %{$token->{attributes}}) {
6963     unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6964 wakaba 1.79 !!!cp ('t343');
6965 wakaba 1.52 $body_el->set_attribute_ns
6966     (undef, [undef, $attr_name],
6967     $token->{attributes}->{$attr_name}->{value});
6968     }
6969     }
6970     }
6971 wakaba 1.125 !!!nack ('t343.1');
6972 wakaba 1.52 !!!next-token;
6973 wakaba 1.126 next B;
6974 wakaba 1.52 } elsif ({
6975 wakaba 1.195 ## NOTE: Start tags for non-phrasing flow content elements
6976    
6977     ## NOTE: The normal one
6978     address => 1, article => 1, aside => 1, blockquote => 1,
6979     center => 1, datagrid => 1, details => 1, dialog => 1,
6980     dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6981     footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6982     h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6983     section => 1, ul => 1,
6984     ## NOTE: As normal, but drops leading newline
6985 wakaba 1.97 pre => 1, listing => 1,
6986 wakaba 1.195 ## NOTE: As normal, but interacts with the form element pointer
6987 wakaba 1.109 form => 1,
6988 wakaba 1.195
6989 wakaba 1.109 table => 1,
6990     hr => 1,
6991 wakaba 1.52 }->{$token->{tag_name}}) {
6992 wakaba 1.109 if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6993     !!!cp ('t350');
6994 wakaba 1.113 !!!parse-error (type => 'in form:form', token => $token);
6995 wakaba 1.109 ## Ignore the token
6996 wakaba 1.125 !!!nack ('t350.1');
6997 wakaba 1.109 !!!next-token;
6998 wakaba 1.126 next B;
6999 wakaba 1.109 }
7000    
7001 wakaba 1.52 ## has a p element in scope
7002     INSCOPE: for (reverse @{$self->{open_elements}}) {
7003 wakaba 1.123 if ($_->[1] & P_EL) {
7004 wakaba 1.79 !!!cp ('t344');
7005 wakaba 1.125 !!!back-token; # <form>
7006 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'p',
7007     line => $token->{line}, column => $token->{column}};
7008 wakaba 1.126 next B;
7009 wakaba 1.123 } elsif ($_->[1] & SCOPING_EL) {
7010 wakaba 1.79 !!!cp ('t345');
7011 wakaba 1.52 last INSCOPE;
7012     }
7013     } # INSCOPE
7014    
7015 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7016 wakaba 1.97 if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
7017 wakaba 1.125 !!!nack ('t346.1');
7018 wakaba 1.52 !!!next-token;
7019 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
7020 wakaba 1.52 $token->{data} =~ s/^\x0A//;
7021     unless (length $token->{data}) {
7022 wakaba 1.79 !!!cp ('t346');
7023 wakaba 1.1 !!!next-token;
7024 wakaba 1.79 } else {
7025     !!!cp ('t349');
7026 wakaba 1.52 }
7027 wakaba 1.79 } else {
7028     !!!cp ('t348');
7029 wakaba 1.52 }
7030 wakaba 1.109 } elsif ($token->{tag_name} eq 'form') {
7031     !!!cp ('t347.1');
7032     $self->{form_element} = $self->{open_elements}->[-1]->[0];
7033    
7034 wakaba 1.125 !!!nack ('t347.2');
7035 wakaba 1.109 !!!next-token;
7036     } elsif ($token->{tag_name} eq 'table') {
7037     !!!cp ('t382');
7038     push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7039    
7040     $self->{insertion_mode} = IN_TABLE_IM;
7041    
7042 wakaba 1.125 !!!nack ('t382.1');
7043 wakaba 1.109 !!!next-token;
7044     } elsif ($token->{tag_name} eq 'hr') {
7045     !!!cp ('t386');
7046     pop @{$self->{open_elements}};
7047    
7048 wakaba 1.125 !!!nack ('t386.1');
7049 wakaba 1.109 !!!next-token;
7050 wakaba 1.52 } else {
7051 wakaba 1.125 !!!nack ('t347.1');
7052 wakaba 1.52 !!!next-token;
7053     }
7054 wakaba 1.126 next B;
7055 wakaba 1.196 } elsif ($token->{tag_name} eq 'li') {
7056     ## NOTE: As normal, but imply </li> when there's another <li> ...
7057 wakaba 1.193
7058     ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7059     ## Interpreted as <li><foo/></li><li/> (non-conforming)
7060     ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7061     ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7062     ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7063     ## object (Fx)
7064     ## Generate non-tree (non-conforming)
7065     ## basefont (IE7 (where basefont is non-void)), center (IE),
7066     ## form (IE), hn (IE)
7067     ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7068     ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7069     ## div (Fx, S)
7070 wakaba 1.196
7071     my $non_optional;
7072 wakaba 1.52 my $i = -1;
7073 wakaba 1.196
7074     ## 1.
7075     for my $node (reverse @{$self->{open_elements}}) {
7076     if ($node->[1] & LI_EL) {
7077     ## 2. (a) As if </li>
7078     {
7079     ## If no </li> - not applied
7080     #
7081    
7082     ## Otherwise
7083    
7084     ## 1. generate implied end tags, except for </li>
7085     #
7086    
7087     ## 2. If current node != "li", parse error
7088     if ($non_optional) {
7089     !!!parse-error (type => 'not closed',
7090     text => $non_optional->[0]->manakai_local_name,
7091     token => $token);
7092     !!!cp ('t355');
7093     } else {
7094     !!!cp ('t356');
7095     }
7096    
7097     ## 3. Pop
7098     splice @{$self->{open_elements}}, $i;
7099 wakaba 1.52 }
7100 wakaba 1.196
7101     last; ## 2. (b) goto 5.
7102     } elsif (
7103     ## NOTE: not "formatting" and not "phrasing"
7104     ($node->[1] & SPECIAL_EL or
7105     $node->[1] & SCOPING_EL) and
7106     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7107    
7108     (not $node->[1] & ADDRESS_EL) &
7109     (not $node->[1] & DIV_EL) &
7110     (not $node->[1] & P_EL)) {
7111     ## 3.
7112 wakaba 1.79 !!!cp ('t357');
7113 wakaba 1.196 last; ## goto 5.
7114     } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7115 wakaba 1.79 !!!cp ('t358');
7116 wakaba 1.196 #
7117     } else {
7118     !!!cp ('t359');
7119     $non_optional ||= $node;
7120     #
7121 wakaba 1.52 }
7122 wakaba 1.196 ## 4.
7123     ## goto 2.
7124 wakaba 1.52 $i--;
7125 wakaba 1.196 }
7126    
7127     ## 5. (a) has a |p| element in scope
7128     INSCOPE: for (reverse @{$self->{open_elements}}) {
7129     if ($_->[1] & P_EL) {
7130     !!!cp ('t353');
7131 wakaba 1.198
7132     ## NOTE: |<p><li>|, for example.
7133    
7134 wakaba 1.196 !!!back-token; # <x>
7135     $token = {type => END_TAG_TOKEN, tag_name => 'p',
7136     line => $token->{line}, column => $token->{column}};
7137     next B;
7138     } elsif ($_->[1] & SCOPING_EL) {
7139     !!!cp ('t354');
7140     last INSCOPE;
7141     }
7142     } # INSCOPE
7143    
7144     ## 5. (b) insert
7145 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7146 wakaba 1.125 !!!nack ('t359.1');
7147 wakaba 1.52 !!!next-token;
7148 wakaba 1.126 next B;
7149 wakaba 1.196 } elsif ($token->{tag_name} eq 'dt' or
7150     $token->{tag_name} eq 'dd') {
7151     ## NOTE: As normal, but imply </dt> or </dd> when ...
7152    
7153     my $non_optional;
7154     my $i = -1;
7155    
7156     ## 1.
7157     for my $node (reverse @{$self->{open_elements}}) {
7158     if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7159     ## 2. (a) As if </li>
7160     {
7161     ## If no </li> - not applied
7162     #
7163    
7164     ## Otherwise
7165    
7166     ## 1. generate implied end tags, except for </dt> or </dd>
7167     #
7168    
7169     ## 2. If current node != "dt"|"dd", parse error
7170     if ($non_optional) {
7171     !!!parse-error (type => 'not closed',
7172     text => $non_optional->[0]->manakai_local_name,
7173     token => $token);
7174     !!!cp ('t355.1');
7175     } else {
7176     !!!cp ('t356.1');
7177     }
7178    
7179     ## 3. Pop
7180     splice @{$self->{open_elements}}, $i;
7181     }
7182    
7183     last; ## 2. (b) goto 5.
7184     } elsif (
7185     ## NOTE: not "formatting" and not "phrasing"
7186     ($node->[1] & SPECIAL_EL or
7187     $node->[1] & SCOPING_EL) and
7188     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7189    
7190     (not $node->[1] & ADDRESS_EL) &
7191     (not $node->[1] & DIV_EL) &
7192     (not $node->[1] & P_EL)) {
7193     ## 3.
7194     !!!cp ('t357.1');
7195     last; ## goto 5.
7196     } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7197     !!!cp ('t358.1');
7198     #
7199     } else {
7200     !!!cp ('t359.1');
7201     $non_optional ||= $node;
7202     #
7203     }
7204     ## 4.
7205     ## goto 2.
7206     $i--;
7207     }
7208    
7209     ## 5. (a) has a |p| element in scope
7210     INSCOPE: for (reverse @{$self->{open_elements}}) {
7211     if ($_->[1] & P_EL) {
7212     !!!cp ('t353.1');
7213     !!!back-token; # <x>
7214     $token = {type => END_TAG_TOKEN, tag_name => 'p',
7215     line => $token->{line}, column => $token->{column}};
7216     next B;
7217     } elsif ($_->[1] & SCOPING_EL) {
7218     !!!cp ('t354.1');
7219     last INSCOPE;
7220     }
7221     } # INSCOPE
7222    
7223     ## 5. (b) insert
7224     !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7225     !!!nack ('t359.2');
7226     !!!next-token;
7227     next B;
7228 wakaba 1.52 } elsif ($token->{tag_name} eq 'plaintext') {
7229 wakaba 1.195 ## NOTE: As normal, but effectively ends parsing
7230    
7231 wakaba 1.52 ## has a p element in scope
7232     INSCOPE: for (reverse @{$self->{open_elements}}) {
7233 wakaba 1.123 if ($_->[1] & P_EL) {
7234 wakaba 1.79 !!!cp ('t367');
7235 wakaba 1.125 !!!back-token; # <plaintext>
7236 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'p',
7237     line => $token->{line}, column => $token->{column}};
7238 wakaba 1.126 next B;
7239 wakaba 1.123 } elsif ($_->[1] & SCOPING_EL) {
7240 wakaba 1.79 !!!cp ('t368');
7241 wakaba 1.52 last INSCOPE;
7242 wakaba 1.46 }
7243 wakaba 1.52 } # INSCOPE
7244    
7245 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7246 wakaba 1.52
7247     $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7248    
7249 wakaba 1.125 !!!nack ('t368.1');
7250 wakaba 1.52 !!!next-token;
7251 wakaba 1.126 next B;
7252 wakaba 1.52 } elsif ($token->{tag_name} eq 'a') {
7253     AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7254     my $node = $active_formatting_elements->[$i];
7255 wakaba 1.123 if ($node->[1] & A_EL) {
7256 wakaba 1.79 !!!cp ('t371');
7257 wakaba 1.113 !!!parse-error (type => 'in a:a', token => $token);
7258 wakaba 1.52
7259 wakaba 1.125 !!!back-token; # <a>
7260 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'a',
7261     line => $token->{line}, column => $token->{column}};
7262 wakaba 1.113 $formatting_end_tag->($token);
7263 wakaba 1.52
7264     AFE2: for (reverse 0..$#$active_formatting_elements) {
7265     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7266 wakaba 1.79 !!!cp ('t372');
7267 wakaba 1.52 splice @$active_formatting_elements, $_, 1;
7268     last AFE2;
7269 wakaba 1.1 }
7270 wakaba 1.52 } # AFE2
7271     OE: for (reverse 0..$#{$self->{open_elements}}) {
7272     if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7273 wakaba 1.79 !!!cp ('t373');
7274 wakaba 1.52 splice @{$self->{open_elements}}, $_, 1;
7275     last OE;
7276 wakaba 1.1 }
7277 wakaba 1.52 } # OE
7278     last AFE;
7279     } elsif ($node->[0] eq '#marker') {
7280 wakaba 1.79 !!!cp ('t374');
7281 wakaba 1.52 last AFE;
7282     }
7283     } # AFE
7284    
7285     $reconstruct_active_formatting_elements->($insert_to_current);
7286 wakaba 1.1
7287 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7288 wakaba 1.52 push @$active_formatting_elements, $self->{open_elements}->[-1];
7289 wakaba 1.1
7290 wakaba 1.125 !!!nack ('t374.1');
7291 wakaba 1.52 !!!next-token;
7292 wakaba 1.126 next B;
7293 wakaba 1.52 } elsif ($token->{tag_name} eq 'nobr') {
7294     $reconstruct_active_formatting_elements->($insert_to_current);
7295 wakaba 1.1
7296 wakaba 1.52 ## has a |nobr| element in scope
7297     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7298     my $node = $self->{open_elements}->[$_];
7299 wakaba 1.123 if ($node->[1] & NOBR_EL) {
7300 wakaba 1.79 !!!cp ('t376');
7301 wakaba 1.113 !!!parse-error (type => 'in nobr:nobr', token => $token);
7302 wakaba 1.125 !!!back-token; # <nobr>
7303 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7304     line => $token->{line}, column => $token->{column}};
7305 wakaba 1.126 next B;
7306 wakaba 1.123 } elsif ($node->[1] & SCOPING_EL) {
7307 wakaba 1.79 !!!cp ('t377');
7308 wakaba 1.52 last INSCOPE;
7309     }
7310     } # INSCOPE
7311    
7312 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7313 wakaba 1.52 push @$active_formatting_elements, $self->{open_elements}->[-1];
7314    
7315 wakaba 1.125 !!!nack ('t377.1');
7316 wakaba 1.52 !!!next-token;
7317 wakaba 1.126 next B;
7318 wakaba 1.52 } elsif ($token->{tag_name} eq 'button') {
7319     ## has a button element in scope
7320     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7321     my $node = $self->{open_elements}->[$_];
7322 wakaba 1.123 if ($node->[1] & BUTTON_EL) {
7323 wakaba 1.79 !!!cp ('t378');
7324 wakaba 1.113 !!!parse-error (type => 'in button:button', token => $token);
7325 wakaba 1.125 !!!back-token; # <button>
7326 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'button',
7327     line => $token->{line}, column => $token->{column}};
7328 wakaba 1.126 next B;
7329 wakaba 1.123 } elsif ($node->[1] & SCOPING_EL) {
7330 wakaba 1.79 !!!cp ('t379');
7331 wakaba 1.52 last INSCOPE;
7332     }
7333     } # INSCOPE
7334    
7335     $reconstruct_active_formatting_elements->($insert_to_current);
7336    
7337 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7338 wakaba 1.85
7339     ## TODO: associate with $self->{form_element} if defined
7340    
7341 wakaba 1.52 push @$active_formatting_elements, ['#marker', ''];
7342 wakaba 1.1
7343 wakaba 1.125 !!!nack ('t379.1');
7344 wakaba 1.52 !!!next-token;
7345 wakaba 1.126 next B;
7346 wakaba 1.103 } elsif ({
7347 wakaba 1.109 xmp => 1,
7348     iframe => 1,
7349     noembed => 1,
7350 wakaba 1.148 noframes => 1, ## NOTE: This is an "as if in head" code clone.
7351 wakaba 1.109 noscript => 0, ## TODO: 1 if scripting is enabled
7352 wakaba 1.103 }->{$token->{tag_name}}) {
7353 wakaba 1.109 if ($token->{tag_name} eq 'xmp') {
7354     !!!cp ('t381');
7355     $reconstruct_active_formatting_elements->($insert_to_current);
7356     } else {
7357     !!!cp ('t399');
7358     }
7359     ## NOTE: There is an "as if in body" code clone.
7360 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
7361 wakaba 1.126 next B;
7362 wakaba 1.52 } elsif ($token->{tag_name} eq 'isindex') {
7363 wakaba 1.113 !!!parse-error (type => 'isindex', token => $token);
7364 wakaba 1.52
7365     if (defined $self->{form_element}) {
7366 wakaba 1.79 !!!cp ('t389');
7367 wakaba 1.52 ## Ignore the token
7368 wakaba 1.125 !!!nack ('t389'); ## NOTE: Not acknowledged.
7369 wakaba 1.52 !!!next-token;
7370 wakaba 1.126 next B;
7371 wakaba 1.52 } else {
7372 wakaba 1.147 !!!ack ('t391.1');
7373    
7374 wakaba 1.52 my $at = $token->{attributes};
7375     my $form_attrs;
7376     $form_attrs->{action} = $at->{action} if $at->{action};
7377     my $prompt_attr = $at->{prompt};
7378     $at->{name} = {name => 'name', value => 'isindex'};
7379     delete $at->{action};
7380     delete $at->{prompt};
7381     my @tokens = (
7382 wakaba 1.55 {type => START_TAG_TOKEN, tag_name => 'form',
7383 wakaba 1.114 attributes => $form_attrs,
7384     line => $token->{line}, column => $token->{column}},
7385     {type => START_TAG_TOKEN, tag_name => 'hr',
7386     line => $token->{line}, column => $token->{column}},
7387     {type => START_TAG_TOKEN, tag_name => 'p',
7388     line => $token->{line}, column => $token->{column}},
7389     {type => START_TAG_TOKEN, tag_name => 'label',
7390     line => $token->{line}, column => $token->{column}},
7391 wakaba 1.52 );
7392     if ($prompt_attr) {
7393 wakaba 1.79 !!!cp ('t390');
7394 wakaba 1.114 push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7395 wakaba 1.118 #line => $token->{line}, column => $token->{column},
7396     };
7397 wakaba 1.1 } else {
7398 wakaba 1.79 !!!cp ('t391');
7399 wakaba 1.55 push @tokens, {type => CHARACTER_TOKEN,
7400 wakaba 1.114 data => 'This is a searchable index. Insert your search keywords here: ',
7401 wakaba 1.118 #line => $token->{line}, column => $token->{column},
7402     }; # SHOULD
7403 wakaba 1.52 ## TODO: make this configurable
7404 wakaba 1.1 }
7405 wakaba 1.52 push @tokens,
7406 wakaba 1.114 {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7407     line => $token->{line}, column => $token->{column}},
7408 wakaba 1.55 #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7409 wakaba 1.114 {type => END_TAG_TOKEN, tag_name => 'label',
7410     line => $token->{line}, column => $token->{column}},
7411     {type => END_TAG_TOKEN, tag_name => 'p',
7412     line => $token->{line}, column => $token->{column}},
7413     {type => START_TAG_TOKEN, tag_name => 'hr',
7414     line => $token->{line}, column => $token->{column}},
7415     {type => END_TAG_TOKEN, tag_name => 'form',
7416     line => $token->{line}, column => $token->{column}};
7417 wakaba 1.52 !!!back-token (@tokens);
7418 wakaba 1.125 !!!next-token;
7419 wakaba 1.126 next B;
7420 wakaba 1.52 }
7421     } elsif ($token->{tag_name} eq 'textarea') {
7422 wakaba 1.205 ## Step 1
7423     !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7424 wakaba 1.52
7425 wakaba 1.205 ## Step 2
7426 wakaba 1.52 ## TODO: $self->{form_element} if defined
7427 wakaba 1.205
7428     ## Step 3
7429     $self->{ignore_newline} = 1;
7430    
7431     ## Step 4
7432     ## ISSUE: This step is wrong. (r2302 enbugged)
7433    
7434     ## Step 5
7435 wakaba 1.52 $self->{content_model} = RCDATA_CONTENT_MODEL;
7436     delete $self->{escape}; # MUST
7437 wakaba 1.205
7438     ## Step 6-7
7439     $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7440    
7441 wakaba 1.125 !!!nack ('t392.1');
7442 wakaba 1.52 !!!next-token;
7443 wakaba 1.126 next B;
7444 wakaba 1.201 } elsif ($token->{tag_name} eq 'optgroup' or
7445     $token->{tag_name} eq 'option') {
7446     ## has an |option| element in scope
7447     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7448     my $node = $self->{open_elements}->[$_];
7449     if ($node->[1] & OPTION_EL) {
7450     !!!cp ('t397.1');
7451     ## NOTE: As if </option>
7452     !!!back-token; # <option> or <optgroup>
7453     $token = {type => END_TAG_TOKEN, tag_name => 'option',
7454     line => $token->{line}, column => $token->{column}};
7455     next B;
7456     } elsif ($node->[1] & SCOPING_EL) {
7457     !!!cp ('t397.2');
7458     last INSCOPE;
7459     }
7460     } # INSCOPE
7461    
7462     $reconstruct_active_formatting_elements->($insert_to_current);
7463    
7464     !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7465    
7466     !!!nack ('t397.3');
7467     !!!next-token;
7468     redo B;
7469 wakaba 1.151 } elsif ($token->{tag_name} eq 'rt' or
7470     $token->{tag_name} eq 'rp') {
7471     ## has a |ruby| element in scope
7472     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7473     my $node = $self->{open_elements}->[$_];
7474     if ($node->[1] & RUBY_EL) {
7475     !!!cp ('t398.1');
7476     ## generate implied end tags
7477     while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7478     !!!cp ('t398.2');
7479     pop @{$self->{open_elements}};
7480     }
7481     unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7482     !!!cp ('t398.3');
7483     !!!parse-error (type => 'not closed',
7484 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
7485 wakaba 1.151 ->manakai_local_name,
7486     token => $token);
7487     pop @{$self->{open_elements}}
7488     while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7489     }
7490     last INSCOPE;
7491     } elsif ($node->[1] & SCOPING_EL) {
7492     !!!cp ('t398.4');
7493     last INSCOPE;
7494     }
7495     } # INSCOPE
7496    
7497     !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7498    
7499     !!!nack ('t398.5');
7500     !!!next-token;
7501     redo B;
7502 wakaba 1.126 } elsif ($token->{tag_name} eq 'math' or
7503     $token->{tag_name} eq 'svg') {
7504     $reconstruct_active_formatting_elements->($insert_to_current);
7505 wakaba 1.131
7506 wakaba 1.155 ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7507    
7508 wakaba 1.131 ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7509    
7510     ## "adjust foreign attributes" - done in insert-element-f
7511 wakaba 1.126
7512 wakaba 1.131 !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7513 wakaba 1.126
7514     if ($self->{self_closing}) {
7515     pop @{$self->{open_elements}};
7516 wakaba 1.201 !!!ack ('t398.6');
7517 wakaba 1.126 } else {
7518 wakaba 1.201 !!!cp ('t398.7');
7519 wakaba 1.126 $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7520     ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7521     ## mode, "in body" (not "in foreign content") secondary insertion
7522     ## mode, maybe.
7523     }
7524    
7525     !!!next-token;
7526     next B;
7527 wakaba 1.52 } elsif ({
7528     caption => 1, col => 1, colgroup => 1, frame => 1,
7529 wakaba 1.201 frameset => 1, head => 1,
7530 wakaba 1.52 tbody => 1, td => 1, tfoot => 1, th => 1,
7531     thead => 1, tr => 1,
7532     }->{$token->{tag_name}}) {
7533 wakaba 1.79 !!!cp ('t401');
7534 wakaba 1.153 !!!parse-error (type => 'in body',
7535     text => $token->{tag_name}, token => $token);
7536 wakaba 1.52 ## Ignore the token
7537 wakaba 1.125 !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7538 wakaba 1.52 !!!next-token;
7539 wakaba 1.126 next B;
7540 wakaba 1.198 } elsif ($token->{tag_name} eq 'param' or
7541     $token->{tag_name} eq 'source') {
7542     !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7543     pop @{$self->{open_elements}};
7544    
7545     !!!ack ('t398.5');
7546     !!!next-token;
7547     redo B;
7548 wakaba 1.52 } else {
7549 wakaba 1.110 if ($token->{tag_name} eq 'image') {
7550     !!!cp ('t384');
7551 wakaba 1.113 !!!parse-error (type => 'image', token => $token);
7552 wakaba 1.110 $token->{tag_name} = 'img';
7553     } else {
7554     !!!cp ('t385');
7555     }
7556    
7557     ## NOTE: There is an "as if <br>" code clone.
7558 wakaba 1.52 $reconstruct_active_formatting_elements->($insert_to_current);
7559    
7560 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7561 wakaba 1.109
7562 wakaba 1.110 if ({
7563     applet => 1, marquee => 1, object => 1,
7564     }->{$token->{tag_name}}) {
7565     !!!cp ('t380');
7566     push @$active_formatting_elements, ['#marker', ''];
7567 wakaba 1.125 !!!nack ('t380.1');
7568 wakaba 1.110 } elsif ({
7569     b => 1, big => 1, em => 1, font => 1, i => 1,
7570 wakaba 1.193 s => 1, small => 1, strike => 1,
7571 wakaba 1.110 strong => 1, tt => 1, u => 1,
7572     }->{$token->{tag_name}}) {
7573     !!!cp ('t375');
7574     push @$active_formatting_elements, $self->{open_elements}->[-1];
7575 wakaba 1.125 !!!nack ('t375.1');
7576 wakaba 1.110 } elsif ($token->{tag_name} eq 'input') {
7577     !!!cp ('t388');
7578     ## TODO: associate with $self->{form_element} if defined
7579     pop @{$self->{open_elements}};
7580 wakaba 1.125 !!!ack ('t388.2');
7581 wakaba 1.110 } elsif ({
7582     area => 1, basefont => 1, bgsound => 1, br => 1,
7583 wakaba 1.198 embed => 1, img => 1, spacer => 1, wbr => 1,
7584 wakaba 1.110 }->{$token->{tag_name}}) {
7585     !!!cp ('t388.1');
7586     pop @{$self->{open_elements}};
7587 wakaba 1.125 !!!ack ('t388.3');
7588 wakaba 1.110 } elsif ($token->{tag_name} eq 'select') {
7589 wakaba 1.109 ## TODO: associate with $self->{form_element} if defined
7590    
7591     if ($self->{insertion_mode} & TABLE_IMS or
7592     $self->{insertion_mode} & BODY_TABLE_IMS or
7593     $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7594     !!!cp ('t400.1');
7595     $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7596     } else {
7597     !!!cp ('t400.2');
7598     $self->{insertion_mode} = IN_SELECT_IM;
7599     }
7600 wakaba 1.125 !!!nack ('t400.3');
7601 wakaba 1.110 } else {
7602 wakaba 1.125 !!!nack ('t402');
7603 wakaba 1.109 }
7604 wakaba 1.51
7605 wakaba 1.52 !!!next-token;
7606 wakaba 1.126 next B;
7607 wakaba 1.52 }
7608 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
7609 wakaba 1.52 if ($token->{tag_name} eq 'body') {
7610 wakaba 1.107 ## has a |body| element in scope
7611     my $i;
7612 wakaba 1.111 INSCOPE: {
7613     for (reverse @{$self->{open_elements}}) {
7614 wakaba 1.123 if ($_->[1] & BODY_EL) {
7615 wakaba 1.111 !!!cp ('t405');
7616     $i = $_;
7617     last INSCOPE;
7618 wakaba 1.123 } elsif ($_->[1] & SCOPING_EL) {
7619 wakaba 1.111 !!!cp ('t405.1');
7620     last;
7621     }
7622 wakaba 1.52 }
7623 wakaba 1.111
7624 wakaba 1.200 ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7625    
7626     !!!parse-error (type => 'unmatched end tag',
7627 wakaba 1.153 text => $token->{tag_name}, token => $token);
7628 wakaba 1.107 ## NOTE: Ignore the token.
7629 wakaba 1.52 !!!next-token;
7630 wakaba 1.126 next B;
7631 wakaba 1.111 } # INSCOPE
7632 wakaba 1.107
7633     for (@{$self->{open_elements}}) {
7634 wakaba 1.123 unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7635 wakaba 1.107 !!!cp ('t403');
7636 wakaba 1.122 !!!parse-error (type => 'not closed',
7637 wakaba 1.153 text => $_->[0]->manakai_local_name,
7638 wakaba 1.122 token => $token);
7639 wakaba 1.107 last;
7640     } else {
7641     !!!cp ('t404');
7642     }
7643     }
7644    
7645     $self->{insertion_mode} = AFTER_BODY_IM;
7646     !!!next-token;
7647 wakaba 1.126 next B;
7648 wakaba 1.52 } elsif ($token->{tag_name} eq 'html') {
7649 wakaba 1.122 ## TODO: Update this code. It seems that the code below is not
7650     ## up-to-date, though it has same effect as speced.
7651 wakaba 1.123 if (@{$self->{open_elements}} > 1 and
7652     $self->{open_elements}->[1]->[1] & BODY_EL) {
7653     unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7654 wakaba 1.79 !!!cp ('t406');
7655 wakaba 1.122 !!!parse-error (type => 'not closed',
7656 wakaba 1.153 text => $self->{open_elements}->[1]->[0]
7657 wakaba 1.122 ->manakai_local_name,
7658     token => $token);
7659 wakaba 1.79 } else {
7660     !!!cp ('t407');
7661 wakaba 1.1 }
7662 wakaba 1.54 $self->{insertion_mode} = AFTER_BODY_IM;
7663 wakaba 1.52 ## reprocess
7664 wakaba 1.126 next B;
7665 wakaba 1.51 } else {
7666 wakaba 1.79 !!!cp ('t408');
7667 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
7668     text => $token->{tag_name}, token => $token);
7669 wakaba 1.52 ## Ignore the token
7670     !!!next-token;
7671 wakaba 1.126 next B;
7672 wakaba 1.51 }
7673 wakaba 1.52 } elsif ({
7674 wakaba 1.195 ## NOTE: End tags for non-phrasing flow content elements
7675    
7676     ## NOTE: The normal ones
7677     address => 1, article => 1, aside => 1, blockquote => 1,
7678     center => 1, datagrid => 1, details => 1, dialog => 1,
7679     dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7680     footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7681     ol => 1, pre => 1, section => 1, ul => 1,
7682    
7683     ## NOTE: As normal, but ... optional tags
7684 wakaba 1.52 dd => 1, dt => 1, li => 1,
7685 wakaba 1.195
7686 wakaba 1.103 applet => 1, button => 1, marquee => 1, object => 1,
7687 wakaba 1.52 }->{$token->{tag_name}}) {
7688 wakaba 1.197 ## NOTE: Code for <li> start tags includes "as if </li>" code.
7689     ## Code for <dt> or <dd> start tags includes "as if </dt> or
7690     ## </dd>" code.
7691    
7692 wakaba 1.52 ## has an element in scope
7693     my $i;
7694     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7695     my $node = $self->{open_elements}->[$_];
7696 wakaba 1.123 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7697 wakaba 1.79 !!!cp ('t410');
7698 wakaba 1.52 $i = $_;
7699 wakaba 1.87 last INSCOPE;
7700 wakaba 1.123 } elsif ($node->[1] & SCOPING_EL) {
7701 wakaba 1.79 !!!cp ('t411');
7702 wakaba 1.52 last INSCOPE;
7703 wakaba 1.51 }
7704 wakaba 1.52 } # INSCOPE
7705 wakaba 1.89
7706     unless (defined $i) { # has an element in scope
7707     !!!cp ('t413');
7708 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
7709     text => $token->{tag_name}, token => $token);
7710 wakaba 1.157 ## NOTE: Ignore the token.
7711 wakaba 1.89 } else {
7712     ## Step 1. generate implied end tags
7713     while ({
7714 wakaba 1.151 ## END_TAG_OPTIONAL_EL
7715 wakaba 1.89 dd => ($token->{tag_name} ne 'dd'),
7716     dt => ($token->{tag_name} ne 'dt'),
7717     li => ($token->{tag_name} ne 'li'),
7718 wakaba 1.194 option => 1,
7719     optgroup => 1,
7720 wakaba 1.89 p => 1,
7721 wakaba 1.151 rt => 1,
7722     rp => 1,
7723 wakaba 1.123 }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7724 wakaba 1.89 !!!cp ('t409');
7725     pop @{$self->{open_elements}};
7726     }
7727    
7728     ## Step 2.
7729 wakaba 1.123 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7730     ne $token->{tag_name}) {
7731 wakaba 1.79 !!!cp ('t412');
7732 wakaba 1.122 !!!parse-error (type => 'not closed',
7733 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
7734 wakaba 1.122 ->manakai_local_name,
7735     token => $token);
7736 wakaba 1.51 } else {
7737 wakaba 1.89 !!!cp ('t414');
7738 wakaba 1.51 }
7739 wakaba 1.89
7740     ## Step 3.
7741 wakaba 1.52 splice @{$self->{open_elements}}, $i;
7742 wakaba 1.89
7743     ## Step 4.
7744     $clear_up_to_marker->()
7745     if {
7746 wakaba 1.103 applet => 1, button => 1, marquee => 1, object => 1,
7747 wakaba 1.89 }->{$token->{tag_name}};
7748 wakaba 1.51 }
7749 wakaba 1.52 !!!next-token;
7750 wakaba 1.126 next B;
7751 wakaba 1.52 } elsif ($token->{tag_name} eq 'form') {
7752 wakaba 1.195 ## NOTE: As normal, but interacts with the form element pointer
7753    
7754 wakaba 1.92 undef $self->{form_element};
7755    
7756 wakaba 1.52 ## has an element in scope
7757 wakaba 1.92 my $i;
7758 wakaba 1.52 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7759     my $node = $self->{open_elements}->[$_];
7760 wakaba 1.123 if ($node->[1] & FORM_EL) {
7761 wakaba 1.79 !!!cp ('t418');
7762 wakaba 1.92 $i = $_;
7763 wakaba 1.52 last INSCOPE;
7764 wakaba 1.123 } elsif ($node->[1] & SCOPING_EL) {
7765 wakaba 1.79 !!!cp ('t419');
7766 wakaba 1.52 last INSCOPE;
7767     }
7768     } # INSCOPE
7769 wakaba 1.92
7770     unless (defined $i) { # has an element in scope
7771 wakaba 1.79 !!!cp ('t421');
7772 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
7773     text => $token->{tag_name}, token => $token);
7774 wakaba 1.157 ## NOTE: Ignore the token.
7775 wakaba 1.92 } else {
7776     ## Step 1. generate implied end tags
7777 wakaba 1.123 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7778 wakaba 1.92 !!!cp ('t417');
7779     pop @{$self->{open_elements}};
7780     }
7781    
7782     ## Step 2.
7783 wakaba 1.123 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7784     ne $token->{tag_name}) {
7785 wakaba 1.92 !!!cp ('t417.1');
7786 wakaba 1.122 !!!parse-error (type => 'not closed',
7787 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
7788 wakaba 1.122 ->manakai_local_name,
7789     token => $token);
7790 wakaba 1.92 } else {
7791     !!!cp ('t420');
7792     }
7793    
7794     ## Step 3.
7795     splice @{$self->{open_elements}}, $i;
7796 wakaba 1.52 }
7797    
7798     !!!next-token;
7799 wakaba 1.126 next B;
7800 wakaba 1.52 } elsif ({
7801 wakaba 1.195 ## NOTE: As normal, except acts as a closer for any ...
7802 wakaba 1.52 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7803     }->{$token->{tag_name}}) {
7804     ## has an element in scope
7805     my $i;
7806     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7807     my $node = $self->{open_elements}->[$_];
7808 wakaba 1.123 if ($node->[1] & HEADING_EL) {
7809 wakaba 1.79 !!!cp ('t423');
7810 wakaba 1.52 $i = $_;
7811     last INSCOPE;
7812 wakaba 1.123 } elsif ($node->[1] & SCOPING_EL) {
7813 wakaba 1.79 !!!cp ('t424');
7814 wakaba 1.52 last INSCOPE;
7815 wakaba 1.51 }
7816 wakaba 1.52 } # INSCOPE
7817 wakaba 1.93
7818     unless (defined $i) { # has an element in scope
7819     !!!cp ('t425.1');
7820 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
7821     text => $token->{tag_name}, token => $token);
7822 wakaba 1.157 ## NOTE: Ignore the token.
7823 wakaba 1.79 } else {
7824 wakaba 1.93 ## Step 1. generate implied end tags
7825 wakaba 1.123 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7826 wakaba 1.93 !!!cp ('t422');
7827     pop @{$self->{open_elements}};
7828     }
7829    
7830     ## Step 2.
7831 wakaba 1.123 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7832     ne $token->{tag_name}) {
7833 wakaba 1.93 !!!cp ('t425');
7834 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
7835     text => $token->{tag_name}, token => $token);
7836 wakaba 1.93 } else {
7837     !!!cp ('t426');
7838     }
7839    
7840     ## Step 3.
7841     splice @{$self->{open_elements}}, $i;
7842 wakaba 1.36 }
7843 wakaba 1.52
7844     !!!next-token;
7845 wakaba 1.126 next B;
7846 wakaba 1.87 } elsif ($token->{tag_name} eq 'p') {
7847 wakaba 1.195 ## NOTE: As normal, except </p> implies <p> and ...
7848    
7849 wakaba 1.87 ## has an element in scope
7850 wakaba 1.197 my $non_optional;
7851 wakaba 1.87 my $i;
7852     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7853     my $node = $self->{open_elements}->[$_];
7854 wakaba 1.123 if ($node->[1] & P_EL) {
7855 wakaba 1.87 !!!cp ('t410.1');
7856     $i = $_;
7857 wakaba 1.88 last INSCOPE;
7858 wakaba 1.123 } elsif ($node->[1] & SCOPING_EL) {
7859 wakaba 1.87 !!!cp ('t411.1');
7860     last INSCOPE;
7861 wakaba 1.197 } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7862     ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7863     !!!cp ('t411.2');
7864     #
7865     } else {
7866     !!!cp ('t411.3');
7867     $non_optional ||= $node;
7868     #
7869 wakaba 1.87 }
7870     } # INSCOPE
7871 wakaba 1.91
7872     if (defined $i) {
7873 wakaba 1.197 ## 1. Generate implied end tags
7874     #
7875    
7876     ## 2. If current node != "p", parse error
7877     if ($non_optional) {
7878 wakaba 1.87 !!!cp ('t412.1');
7879 wakaba 1.122 !!!parse-error (type => 'not closed',
7880 wakaba 1.197 text => $non_optional->[0]->manakai_local_name,
7881 wakaba 1.122 token => $token);
7882 wakaba 1.87 } else {
7883 wakaba 1.91 !!!cp ('t414.1');
7884 wakaba 1.87 }
7885 wakaba 1.91
7886 wakaba 1.197 ## 3. Pop
7887 wakaba 1.87 splice @{$self->{open_elements}}, $i;
7888     } else {
7889 wakaba 1.91 !!!cp ('t413.1');
7890 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
7891     text => $token->{tag_name}, token => $token);
7892 wakaba 1.91
7893 wakaba 1.87 !!!cp ('t415.1');
7894     ## As if <p>, then reprocess the current token
7895     my $el;
7896 wakaba 1.126 !!!create-element ($el, $HTML_NS, 'p',, $token);
7897 wakaba 1.87 $insert->($el);
7898 wakaba 1.91 ## NOTE: Not inserted into |$self->{open_elements}|.
7899 wakaba 1.87 }
7900 wakaba 1.91
7901 wakaba 1.87 !!!next-token;
7902 wakaba 1.126 next B;
7903 wakaba 1.52 } elsif ({
7904     a => 1,
7905     b => 1, big => 1, em => 1, font => 1, i => 1,
7906 wakaba 1.193 nobr => 1, s => 1, small => 1, strike => 1,
7907 wakaba 1.52 strong => 1, tt => 1, u => 1,
7908     }->{$token->{tag_name}}) {
7909 wakaba 1.79 !!!cp ('t427');
7910 wakaba 1.113 $formatting_end_tag->($token);
7911 wakaba 1.126 next B;
7912 wakaba 1.52 } elsif ($token->{tag_name} eq 'br') {
7913 wakaba 1.79 !!!cp ('t428');
7914 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
7915     text => 'br', token => $token);
7916 wakaba 1.52
7917     ## As if <br>
7918     $reconstruct_active_formatting_elements->($insert_to_current);
7919    
7920     my $el;
7921 wakaba 1.126 !!!create-element ($el, $HTML_NS, 'br',, $token);
7922 wakaba 1.52 $insert->($el);
7923    
7924     ## Ignore the token.
7925     !!!next-token;
7926 wakaba 1.126 next B;
7927 wakaba 1.52 } else {
7928 wakaba 1.195 if ($token->{tag_name} eq 'sarcasm') {
7929     sleep 0.001; # take a deep breath
7930     }
7931    
7932 wakaba 1.52 ## Step 1
7933     my $node_i = -1;
7934     my $node = $self->{open_elements}->[$node_i];
7935 wakaba 1.51
7936 wakaba 1.52 ## Step 2
7937     S2: {
7938 wakaba 1.200 my $node_tag_name = $node->[0]->manakai_local_name;
7939     $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7940     if ($node_tag_name eq $token->{tag_name}) {
7941 wakaba 1.52 ## Step 1
7942     ## generate implied end tags
7943 wakaba 1.123 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7944 wakaba 1.79 !!!cp ('t430');
7945 wakaba 1.151 ## NOTE: |<ruby><rt></ruby>|.
7946     ## ISSUE: <ruby><rt></rt> will also take this code path,
7947     ## which seems wrong.
7948 wakaba 1.86 pop @{$self->{open_elements}};
7949 wakaba 1.151 $node_i++;
7950 wakaba 1.52 }
7951    
7952     ## Step 2
7953 wakaba 1.200 my $current_tag_name
7954     = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7955     $current_tag_name =~ tr/A-Z/a-z/;
7956     if ($current_tag_name ne $token->{tag_name}) {
7957 wakaba 1.79 !!!cp ('t431');
7958 wakaba 1.58 ## NOTE: <x><y></x>
7959 wakaba 1.122 !!!parse-error (type => 'not closed',
7960 wakaba 1.153 text => $self->{open_elements}->[-1]->[0]
7961 wakaba 1.122 ->manakai_local_name,
7962     token => $token);
7963 wakaba 1.79 } else {
7964     !!!cp ('t432');
7965 wakaba 1.52 }
7966    
7967     ## Step 3
7968 wakaba 1.151 splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7969 wakaba 1.51
7970 wakaba 1.1 !!!next-token;
7971 wakaba 1.52 last S2;
7972 wakaba 1.1 } else {
7973 wakaba 1.52 ## Step 3
7974 wakaba 1.123 if (not ($node->[1] & FORMATTING_EL) and
7975 wakaba 1.52 #not $phrasing_category->{$node->[1]} and
7976 wakaba 1.123 ($node->[1] & SPECIAL_EL or
7977     $node->[1] & SCOPING_EL)) {
7978 wakaba 1.79 !!!cp ('t433');
7979 wakaba 1.153 !!!parse-error (type => 'unmatched end tag',
7980     text => $token->{tag_name}, token => $token);
7981 wakaba 1.52 ## Ignore the token
7982     !!!next-token;
7983     last S2;
7984 wakaba 1.193
7985     ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7986     ## 9.27, "a" is a child of <dd> (conforming). In
7987     ## Firefox 3.0.2, "a" is a child of <body>. In WinIE 7,
7988     ## "a" is a child of both <body> and <dd>.
7989 wakaba 1.52 }
7990 wakaba 1.193
7991 wakaba 1.79 !!!cp ('t434');
7992 wakaba 1.1 }
7993 wakaba 1.52
7994     ## Step 4
7995     $node_i--;
7996     $node = $self->{open_elements}->[$node_i];
7997    
7998     ## Step 5;
7999     redo S2;
8000     } # S2
8001 wakaba 1.126 next B;
8002 wakaba 1.1 }
8003     }
8004 wakaba 1.126 next B;
8005     } continue { # B
8006     if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8007     ## NOTE: The code below is executed in cases where it does not have
8008     ## to be, but it it is harmless even in those cases.
8009     ## has an element in scope
8010     INSCOPE: {
8011     for (reverse 0..$#{$self->{open_elements}}) {
8012     my $node = $self->{open_elements}->[$_];
8013     if ($node->[1] & FOREIGN_EL) {
8014     last INSCOPE;
8015     } elsif ($node->[1] & SCOPING_EL) {
8016     last;
8017     }
8018     }
8019    
8020     ## NOTE: No foreign element in scope.
8021     $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8022     } # INSCOPE
8023     }
8024 wakaba 1.1 } # B
8025    
8026     ## Stop parsing # MUST
8027    
8028     ## TODO: script stuffs
8029 wakaba 1.3 } # _tree_construct_main
8030    
8031 wakaba 1.177 sub set_inner_html ($$$$;$) {
8032 wakaba 1.3 my $class = shift;
8033     my $node = shift;
8034 wakaba 1.177 #my $s = \$_[0];
8035 wakaba 1.3 my $onerror = $_[1];
8036 wakaba 1.162 my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8037 wakaba 1.3
8038 wakaba 1.63 ## ISSUE: Should {confident} be true?
8039    
8040 wakaba 1.3 my $nt = $node->node_type;
8041     if ($nt == 9) {
8042     # MUST
8043    
8044     ## Step 1 # MUST
8045     ## TODO: If the document has an active parser, ...
8046     ## ISSUE: There is an issue in the spec.
8047    
8048     ## Step 2 # MUST
8049     my @cn = @{$node->child_nodes};
8050     for (@cn) {
8051     $node->remove_child ($_);
8052     }
8053    
8054     ## Step 3, 4, 5 # MUST
8055 wakaba 1.177 $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8056 wakaba 1.3 } elsif ($nt == 1) {
8057     ## TODO: If non-html element
8058    
8059     ## NOTE: Most of this code is copied from |parse_string|
8060    
8061 wakaba 1.162 ## TODO: Support for $get_wrapper
8062    
8063 wakaba 1.3 ## Step 1 # MUST
8064 wakaba 1.14 my $this_doc = $node->owner_document;
8065     my $doc = $this_doc->implementation->create_document;
8066 wakaba 1.18 $doc->manakai_is_html (1);
8067 wakaba 1.3 my $p = $class->new;
8068     $p->{document} = $doc;
8069    
8070 wakaba 1.84 ## Step 8 # MUST
8071 wakaba 1.3 my $i = 0;
8072 wakaba 1.121 $p->{line_prev} = $p->{line} = 1;
8073     $p->{column_prev} = $p->{column} = 0;
8074 wakaba 1.177 require Whatpm::Charset::DecodeHandle;
8075     my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8076     $input = $get_wrapper->($input);
8077 wakaba 1.183 $p->{set_nc} = sub {
8078 wakaba 1.3 my $self = shift;
8079 wakaba 1.14
8080 wakaba 1.178 my $char = '';
8081 wakaba 1.183 if (defined $self->{next_nc}) {
8082     $char = $self->{next_nc};
8083     delete $self->{next_nc};
8084     $self->{nc} = ord $char;
8085 wakaba 1.177 } else {
8086 wakaba 1.180 $self->{char_buffer} = '';
8087     $self->{char_buffer_pos} = 0;
8088    
8089     my $count = $input->manakai_read_until
8090 wakaba 1.182 ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8091     $self->{char_buffer_pos});
8092 wakaba 1.180 if ($count) {
8093     $self->{line_prev} = $self->{line};
8094     $self->{column_prev} = $self->{column};
8095     $self->{column}++;
8096 wakaba 1.183 $self->{nc}
8097 wakaba 1.180 = ord substr ($self->{char_buffer},
8098     $self->{char_buffer_pos}++, 1);
8099     return;
8100     }
8101    
8102 wakaba 1.178 if ($input->read ($char, 1)) {
8103 wakaba 1.183 $self->{nc} = ord $char;
8104 wakaba 1.178 } else {
8105 wakaba 1.183 $self->{nc} = -1;
8106 wakaba 1.178 return;
8107     }
8108 wakaba 1.177 }
8109 wakaba 1.121
8110     ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8111     $p->{column}++;
8112 wakaba 1.4
8113 wakaba 1.183 if ($self->{nc} == 0x000A) { # LF
8114 wakaba 1.121 $p->{line}++;
8115     $p->{column} = 0;
8116 wakaba 1.79 !!!cp ('i1');
8117 wakaba 1.183 } elsif ($self->{nc} == 0x000D) { # CR
8118 wakaba 1.177 ## TODO: support for abort/streaming
8119 wakaba 1.178 my $next = '';
8120     if ($input->read ($next, 1) and $next ne "\x0A") {
8121 wakaba 1.183 $self->{next_nc} = $next;
8122 wakaba 1.177 }
8123 wakaba 1.183 $self->{nc} = 0x000A; # LF # MUST
8124 wakaba 1.121 $p->{line}++;
8125     $p->{column} = 0;
8126 wakaba 1.79 !!!cp ('i2');
8127 wakaba 1.183 } elsif ($self->{nc} == 0x0000) { # NULL
8128 wakaba 1.79 !!!cp ('i4');
8129 wakaba 1.14 !!!parse-error (type => 'NULL');
8130 wakaba 1.183 $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8131 wakaba 1.3 }
8132     };
8133 wakaba 1.171
8134 wakaba 1.172 $p->{read_until} = sub {
8135 wakaba 1.177 #my ($scalar, $specials_range, $offset) = @_;
8136 wakaba 1.183 return 0 if defined $p->{next_nc};
8137 wakaba 1.180
8138 wakaba 1.182 my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8139 wakaba 1.180 my $offset = $_[2] || 0;
8140    
8141     if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8142     pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8143     if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8144     substr ($_[0], $offset)
8145     = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8146     my $count = $+[0] - $-[0];
8147     if ($count) {
8148     $p->{column} += $count;
8149     $p->{char_buffer_pos} += $count;
8150     $p->{line_prev} = $p->{line};
8151     $p->{column_prev} = $p->{column} - 1;
8152 wakaba 1.183 $p->{nc} = -1;
8153 wakaba 1.180 }
8154     return $count;
8155     } else {
8156     return 0;
8157     }
8158     } else {
8159     my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8160     if ($count) {
8161     $p->{column} += $count;
8162     $p->{column_prev} += $count;
8163 wakaba 1.183 $p->{nc} = -1;
8164 wakaba 1.180 }
8165     return $count;
8166 wakaba 1.177 }
8167     }; # $p->{read_until}
8168 wakaba 1.171
8169 wakaba 1.3 my $ponerror = $onerror || sub {
8170     my (%opt) = @_;
8171 wakaba 1.121 my $line = $opt{line};
8172     my $column = $opt{column};
8173     if (defined $opt{token} and defined $opt{token}->{line}) {
8174     $line = $opt{token}->{line};
8175     $column = $opt{token}->{column};
8176     }
8177     warn "Parse error ($opt{type}) at line $line column $column\n";
8178 wakaba 1.3 };
8179     $p->{parse_error} = sub {
8180 wakaba 1.121 $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8181 wakaba 1.3 };
8182    
8183 wakaba 1.178 my $char_onerror = sub {
8184     my (undef, $type, %opt) = @_;
8185     $ponerror->(layer => 'encode',
8186     line => $p->{line}, column => $p->{column} + 1,
8187     %opt, type => $type);
8188     }; # $char_onerror
8189     $input->onerror ($char_onerror);
8190    
8191 wakaba 1.3 $p->_initialize_tokenizer;
8192     $p->_initialize_tree_constructor;
8193    
8194     ## Step 2
8195 wakaba 1.71 my $node_ln = $node->manakai_local_name;
8196 wakaba 1.40 $p->{content_model} = {
8197     title => RCDATA_CONTENT_MODEL,
8198     textarea => RCDATA_CONTENT_MODEL,
8199     style => CDATA_CONTENT_MODEL,
8200     script => CDATA_CONTENT_MODEL,
8201     xmp => CDATA_CONTENT_MODEL,
8202     iframe => CDATA_CONTENT_MODEL,
8203     noembed => CDATA_CONTENT_MODEL,
8204     noframes => CDATA_CONTENT_MODEL,
8205     noscript => CDATA_CONTENT_MODEL,
8206     plaintext => PLAINTEXT_CONTENT_MODEL,
8207     }->{$node_ln};
8208     $p->{content_model} = PCDATA_CONTENT_MODEL
8209     unless defined $p->{content_model};
8210     ## ISSUE: What is "the name of the element"? local name?
8211 wakaba 1.3
8212 wakaba 1.123 $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8213     ## TODO: Foreign element OK?
8214 wakaba 1.3
8215 wakaba 1.84 ## Step 3
8216 wakaba 1.3 my $root = $doc->create_element_ns
8217     ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8218    
8219 wakaba 1.84 ## Step 4 # MUST
8220 wakaba 1.3 $doc->append_child ($root);
8221    
8222 wakaba 1.84 ## Step 5 # MUST
8223 wakaba 1.123 push @{$p->{open_elements}}, [$root, $el_category->{html}];
8224 wakaba 1.3
8225     undef $p->{head_element};
8226 wakaba 1.202 undef $p->{head_element_inserted};
8227 wakaba 1.3
8228 wakaba 1.84 ## Step 6 # MUST
8229 wakaba 1.3 $p->_reset_insertion_mode;
8230    
8231 wakaba 1.84 ## Step 7 # MUST
8232 wakaba 1.3 my $anode = $node;
8233     AN: while (defined $anode) {
8234     if ($anode->node_type == 1) {
8235     my $nsuri = $anode->namespace_uri;
8236     if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8237 wakaba 1.71 if ($anode->manakai_local_name eq 'form') {
8238 wakaba 1.79 !!!cp ('i5');
8239 wakaba 1.3 $p->{form_element} = $anode;
8240     last AN;
8241     }
8242     }
8243     }
8244     $anode = $anode->parent_node;
8245     } # AN
8246    
8247 wakaba 1.84 ## Step 9 # MUST
8248 wakaba 1.3 {
8249     my $self = $p;
8250     !!!next-token;
8251     }
8252     $p->_tree_construction_main;
8253    
8254 wakaba 1.84 ## Step 10 # MUST
8255 wakaba 1.3 my @cn = @{$node->child_nodes};
8256     for (@cn) {
8257     $node->remove_child ($_);
8258     }
8259     ## ISSUE: mutation events? read-only?
8260    
8261 wakaba 1.84 ## Step 11 # MUST
8262 wakaba 1.3 @cn = @{$root->child_nodes};
8263     for (@cn) {
8264 wakaba 1.14 $this_doc->adopt_node ($_);
8265 wakaba 1.3 $node->append_child ($_);
8266     }
8267 wakaba 1.14 ## ISSUE: mutation events?
8268 wakaba 1.3
8269     $p->_terminate_tree_constructor;
8270 wakaba 1.121
8271     delete $p->{parse_error}; # delete loop
8272 wakaba 1.3 } else {
8273     die "$0: |set_inner_html| is not defined for node of type $nt";
8274     }
8275     } # set_inner_html
8276    
8277     } # tree construction stage
8278 wakaba 1.1
8279 wakaba 1.63 package Whatpm::HTML::RestartParser;
8280     push our @ISA, 'Error';
8281    
8282 wakaba 1.1 1;
8283 wakaba 1.205 # $Date: 2008/10/05 05:59:35 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24