/[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.204 - (hide annotations) (download) (as text)
Sun Oct 5 05:59:35 2008 UTC (18 years ago) by wakaba
Branch: MAIN
Changes since 1.203: +15 -14 lines
File MIME type: application/x-wais-source
++ whatpm/t/ChangeLog	5 Oct 2008 05:38:58 -0000
	* content-model-1.dat, content-model-2.dat: Test results on
	|label| contents fixed.

	* ContentChecker.t: |dom-conformance/html-form-label.dat| added.

	* dom-conformance/: New directory.

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

++ whatpm/t/dom-conformance/ChangeLog	5 Oct 2008 05:35:48 -0000
2008-10-05  Wakaba  <wakaba@suika.fam.cx>

	* html-form-label.dat: New test data file.

	* ChangeLog: New file.

++ whatpm/Whatpm/ChangeLog	5 Oct 2008 05:58:50 -0000
	* ContentChecker.pod: Note on internal flags is added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	5 Oct 2008 05:36:36 -0000
2008-10-05  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: <label> content conformance checking implemented.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24