/[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.207 - (hide annotations) (download) (as text)
Mon Oct 13 08:27:44 2008 UTC (17 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.206: +6 -12 lines
File MIME type: application/x-wais-source
++ whatpm/Whatpm/ChangeLog	13 Oct 2008 08:27:39 -0000
	* HTML.pm.src: Merge |DT_EL| and |DD_EL| as |DTDD_EL|.

2008-10-13  Wakaba  <wakaba@suika.fam.cx>

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24