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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.178 - (hide annotations) (download)
Mon Sep 15 07:19:03 2008 UTC (17 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.177: +24 -77 lines
++ whatpm/Whatpm/ChangeLog	15 Sep 2008 07:17:34 -0000
	* HTML.pm.src: Remove checking for control character, surrogate
	pair, or noncharacter code points and non-Unicode code
	points (they should be handled by Whatpm::Charset::UnicodeChecker).
	(parse_char_stream): Support for the |$get_wrapper| argument and
	character stream error handlers.

2008-09-15  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/Charset/ChangeLog	15 Sep 2008 07:18:45 -0000
	* DecodeHandle.pm (onerror): Return |undef| if no explicit value
	is set.

	* UnicodeChecker.pm: Support for HTML5 parse errors.
	(onerror): Return |undef| if no explicit value is set.

2008-09-15  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.2 package Whatpm::HTML;
2 wakaba 1.1 use strict;
3 wakaba 1.178 our $VERSION=do{my @r=(q$Revision: 1.181 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 wakaba 1.65 use Error qw(:try);
5 wakaba 1.1
6 wakaba 1.18 ## ISSUE:
7     ## var doc = implementation.createDocument (null, null, null);
8     ## doc.write ('');
9     ## alert (doc.compatMode);
10 wakaba 1.1
11 wakaba 1.136 require IO::Handle;
12    
13 wakaba 1.123 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
14     my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
15     my $SVG_NS = q<http://www.w3.org/2000/svg>;
16     my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
17     my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
18     my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
19    
20 wakaba 1.121 sub A_EL () { 0b1 }
21     sub ADDRESS_EL () { 0b10 }
22     sub BODY_EL () { 0b100 }
23     sub BUTTON_EL () { 0b1000 }
24     sub CAPTION_EL () { 0b10000 }
25     sub DD_EL () { 0b100000 }
26     sub DIV_EL () { 0b1000000 }
27     sub DT_EL () { 0b10000000 }
28     sub FORM_EL () { 0b100000000 }
29     sub FORMATTING_EL () { 0b1000000000 }
30     sub FRAMESET_EL () { 0b10000000000 }
31     sub HEADING_EL () { 0b100000000000 }
32     sub HTML_EL () { 0b1000000000000 }
33     sub LI_EL () { 0b10000000000000 }
34     sub NOBR_EL () { 0b100000000000000 }
35     sub OPTION_EL () { 0b1000000000000000 }
36     sub OPTGROUP_EL () { 0b10000000000000000 }
37     sub P_EL () { 0b100000000000000000 }
38     sub SELECT_EL () { 0b1000000000000000000 }
39     sub TABLE_EL () { 0b10000000000000000000 }
40     sub TABLE_CELL_EL () { 0b100000000000000000000 }
41     sub TABLE_ROW_EL () { 0b1000000000000000000000 }
42     sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
43     sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
44     sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
45 wakaba 1.123 sub FOREIGN_EL () { 0b10000000000000000000000000 }
46     sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
47     sub MML_AXML_EL () { 0b1000000000000000000000000000 }
48 wakaba 1.149 sub RUBY_EL () { 0b10000000000000000000000000000 }
49     sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
50 wakaba 1.121
51     sub TABLE_ROWS_EL () {
52     TABLE_EL |
53     TABLE_ROW_EL |
54     TABLE_ROW_GROUP_EL
55     }
56    
57 wakaba 1.149 ## NOTE: Used in "generate implied end tags" algorithm.
58     ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL
59     ## is used in "generate implied end tags" implementation (search for the
60     ## function mae).
61 wakaba 1.121 sub END_TAG_OPTIONAL_EL () {
62     DD_EL |
63     DT_EL |
64     LI_EL |
65 wakaba 1.149 P_EL |
66     RUBY_COMPONENT_EL
67 wakaba 1.121 }
68    
69 wakaba 1.149 ## NOTE: Used in </body> and EOF algorithms.
70 wakaba 1.121 sub ALL_END_TAG_OPTIONAL_EL () {
71 wakaba 1.149 DD_EL |
72     DT_EL |
73     LI_EL |
74     P_EL |
75    
76 wakaba 1.121 BODY_EL |
77     HTML_EL |
78     TABLE_CELL_EL |
79     TABLE_ROW_EL |
80     TABLE_ROW_GROUP_EL
81     }
82    
83     sub SCOPING_EL () {
84     BUTTON_EL |
85     CAPTION_EL |
86     HTML_EL |
87     TABLE_EL |
88     TABLE_CELL_EL |
89     MISC_SCOPING_EL
90     }
91    
92     sub TABLE_SCOPING_EL () {
93     HTML_EL |
94     TABLE_EL
95     }
96    
97     sub TABLE_ROWS_SCOPING_EL () {
98     HTML_EL |
99     TABLE_ROW_GROUP_EL
100     }
101    
102     sub TABLE_ROW_SCOPING_EL () {
103     HTML_EL |
104     TABLE_ROW_EL
105     }
106    
107     sub SPECIAL_EL () {
108     ADDRESS_EL |
109     BODY_EL |
110     DIV_EL |
111 wakaba 1.149
112     DD_EL |
113     DT_EL |
114     LI_EL |
115     P_EL |
116    
117 wakaba 1.121 FORM_EL |
118     FRAMESET_EL |
119     HEADING_EL |
120     OPTION_EL |
121     OPTGROUP_EL |
122     SELECT_EL |
123     TABLE_ROW_EL |
124     TABLE_ROW_GROUP_EL |
125     MISC_SPECIAL_EL
126     }
127    
128     my $el_category = {
129     a => A_EL | FORMATTING_EL,
130     address => ADDRESS_EL,
131     applet => MISC_SCOPING_EL,
132     area => MISC_SPECIAL_EL,
133     b => FORMATTING_EL,
134     base => MISC_SPECIAL_EL,
135     basefont => MISC_SPECIAL_EL,
136     bgsound => MISC_SPECIAL_EL,
137     big => FORMATTING_EL,
138     blockquote => MISC_SPECIAL_EL,
139     body => BODY_EL,
140     br => MISC_SPECIAL_EL,
141     button => BUTTON_EL,
142     caption => CAPTION_EL,
143     center => MISC_SPECIAL_EL,
144     col => MISC_SPECIAL_EL,
145     colgroup => MISC_SPECIAL_EL,
146     dd => DD_EL,
147     dir => MISC_SPECIAL_EL,
148     div => DIV_EL,
149     dl => MISC_SPECIAL_EL,
150     dt => DT_EL,
151     em => FORMATTING_EL,
152     embed => MISC_SPECIAL_EL,
153     fieldset => MISC_SPECIAL_EL,
154     font => FORMATTING_EL,
155     form => FORM_EL,
156     frame => MISC_SPECIAL_EL,
157     frameset => FRAMESET_EL,
158     h1 => HEADING_EL,
159     h2 => HEADING_EL,
160     h3 => HEADING_EL,
161     h4 => HEADING_EL,
162     h5 => HEADING_EL,
163     h6 => HEADING_EL,
164     head => MISC_SPECIAL_EL,
165     hr => MISC_SPECIAL_EL,
166     html => HTML_EL,
167     i => FORMATTING_EL,
168     iframe => MISC_SPECIAL_EL,
169     img => MISC_SPECIAL_EL,
170     input => MISC_SPECIAL_EL,
171     isindex => MISC_SPECIAL_EL,
172     li => LI_EL,
173     link => MISC_SPECIAL_EL,
174     listing => MISC_SPECIAL_EL,
175     marquee => MISC_SCOPING_EL,
176     menu => MISC_SPECIAL_EL,
177     meta => MISC_SPECIAL_EL,
178     nobr => NOBR_EL | FORMATTING_EL,
179     noembed => MISC_SPECIAL_EL,
180     noframes => MISC_SPECIAL_EL,
181     noscript => MISC_SPECIAL_EL,
182     object => MISC_SCOPING_EL,
183     ol => MISC_SPECIAL_EL,
184     optgroup => OPTGROUP_EL,
185     option => OPTION_EL,
186     p => P_EL,
187     param => MISC_SPECIAL_EL,
188     plaintext => MISC_SPECIAL_EL,
189     pre => MISC_SPECIAL_EL,
190 wakaba 1.149 rp => RUBY_COMPONENT_EL,
191     rt => RUBY_COMPONENT_EL,
192     ruby => RUBY_EL,
193 wakaba 1.121 s => FORMATTING_EL,
194     script => MISC_SPECIAL_EL,
195     select => SELECT_EL,
196     small => FORMATTING_EL,
197     spacer => MISC_SPECIAL_EL,
198     strike => FORMATTING_EL,
199     strong => FORMATTING_EL,
200     style => MISC_SPECIAL_EL,
201     table => TABLE_EL,
202     tbody => TABLE_ROW_GROUP_EL,
203     td => TABLE_CELL_EL,
204     textarea => MISC_SPECIAL_EL,
205     tfoot => TABLE_ROW_GROUP_EL,
206     th => TABLE_CELL_EL,
207     thead => TABLE_ROW_GROUP_EL,
208     title => MISC_SPECIAL_EL,
209     tr => TABLE_ROW_EL,
210     tt => FORMATTING_EL,
211     u => FORMATTING_EL,
212     ul => MISC_SPECIAL_EL,
213     wbr => MISC_SPECIAL_EL,
214     };
215    
216 wakaba 1.123 my $el_category_f = {
217     $MML_NS => {
218     'annotation-xml' => MML_AXML_EL,
219     mi => FOREIGN_FLOW_CONTENT_EL,
220     mo => FOREIGN_FLOW_CONTENT_EL,
221     mn => FOREIGN_FLOW_CONTENT_EL,
222     ms => FOREIGN_FLOW_CONTENT_EL,
223     mtext => FOREIGN_FLOW_CONTENT_EL,
224     },
225     $SVG_NS => {
226 wakaba 1.128 foreignObject => FOREIGN_FLOW_CONTENT_EL,
227 wakaba 1.123 desc => FOREIGN_FLOW_CONTENT_EL,
228     title => FOREIGN_FLOW_CONTENT_EL,
229     },
230     ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
231     };
232    
233 wakaba 1.128 my $svg_attr_name = {
234 wakaba 1.143 attributename => 'attributeName',
235 wakaba 1.128 attributetype => 'attributeType',
236     basefrequency => 'baseFrequency',
237     baseprofile => 'baseProfile',
238     calcmode => 'calcMode',
239     clippathunits => 'clipPathUnits',
240     contentscripttype => 'contentScriptType',
241     contentstyletype => 'contentStyleType',
242     diffuseconstant => 'diffuseConstant',
243     edgemode => 'edgeMode',
244     externalresourcesrequired => 'externalResourcesRequired',
245     filterres => 'filterRes',
246     filterunits => 'filterUnits',
247     glyphref => 'glyphRef',
248     gradienttransform => 'gradientTransform',
249     gradientunits => 'gradientUnits',
250     kernelmatrix => 'kernelMatrix',
251     kernelunitlength => 'kernelUnitLength',
252     keypoints => 'keyPoints',
253     keysplines => 'keySplines',
254     keytimes => 'keyTimes',
255     lengthadjust => 'lengthAdjust',
256     limitingconeangle => 'limitingConeAngle',
257     markerheight => 'markerHeight',
258     markerunits => 'markerUnits',
259     markerwidth => 'markerWidth',
260     maskcontentunits => 'maskContentUnits',
261     maskunits => 'maskUnits',
262     numoctaves => 'numOctaves',
263     pathlength => 'pathLength',
264     patterncontentunits => 'patternContentUnits',
265     patterntransform => 'patternTransform',
266     patternunits => 'patternUnits',
267     pointsatx => 'pointsAtX',
268     pointsaty => 'pointsAtY',
269     pointsatz => 'pointsAtZ',
270     preservealpha => 'preserveAlpha',
271     preserveaspectratio => 'preserveAspectRatio',
272     primitiveunits => 'primitiveUnits',
273     refx => 'refX',
274     refy => 'refY',
275     repeatcount => 'repeatCount',
276     repeatdur => 'repeatDur',
277     requiredextensions => 'requiredExtensions',
278 wakaba 1.143 requiredfeatures => 'requiredFeatures',
279 wakaba 1.128 specularconstant => 'specularConstant',
280     specularexponent => 'specularExponent',
281     spreadmethod => 'spreadMethod',
282     startoffset => 'startOffset',
283     stddeviation => 'stdDeviation',
284     stitchtiles => 'stitchTiles',
285     surfacescale => 'surfaceScale',
286     systemlanguage => 'systemLanguage',
287     tablevalues => 'tableValues',
288     targetx => 'targetX',
289     targety => 'targetY',
290     textlength => 'textLength',
291     viewbox => 'viewBox',
292     viewtarget => 'viewTarget',
293     xchannelselector => 'xChannelSelector',
294     ychannelselector => 'yChannelSelector',
295     zoomandpan => 'zoomAndPan',
296     };
297    
298     my $foreign_attr_xname = {
299     'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
300     'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
301     'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
302     'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
303     'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
304     'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
305     'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
306     'xml:base' => [$XML_NS, ['xml', 'base']],
307     'xml:lang' => [$XML_NS, ['xml', 'lang']],
308     'xml:space' => [$XML_NS, ['xml', 'space']],
309     'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
310     'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
311     };
312    
313 wakaba 1.129 ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
314    
315 wakaba 1.4 my $c1_entity_char = {
316 wakaba 1.9 0x80 => 0x20AC,
317     0x81 => 0xFFFD,
318     0x82 => 0x201A,
319     0x83 => 0x0192,
320     0x84 => 0x201E,
321     0x85 => 0x2026,
322     0x86 => 0x2020,
323     0x87 => 0x2021,
324     0x88 => 0x02C6,
325     0x89 => 0x2030,
326     0x8A => 0x0160,
327     0x8B => 0x2039,
328     0x8C => 0x0152,
329     0x8D => 0xFFFD,
330     0x8E => 0x017D,
331     0x8F => 0xFFFD,
332     0x90 => 0xFFFD,
333     0x91 => 0x2018,
334     0x92 => 0x2019,
335     0x93 => 0x201C,
336     0x94 => 0x201D,
337     0x95 => 0x2022,
338     0x96 => 0x2013,
339     0x97 => 0x2014,
340     0x98 => 0x02DC,
341     0x99 => 0x2122,
342     0x9A => 0x0161,
343     0x9B => 0x203A,
344     0x9C => 0x0153,
345     0x9D => 0xFFFD,
346     0x9E => 0x017E,
347     0x9F => 0x0178,
348 wakaba 1.4 }; # $c1_entity_char
349 wakaba 1.1
350 wakaba 1.65 sub parse_byte_string ($$$$;$) {
351 wakaba 1.135 my $self = shift;
352     my $charset_name = shift;
353     open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
354     return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
355     } # parse_byte_string
356    
357 wakaba 1.158 sub parse_byte_stream ($$$$;$$) {
358     # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
359 wakaba 1.65 my $self = ref $_[0] ? shift : shift->new;
360 wakaba 1.130 my $charset_name = shift;
361 wakaba 1.135 my $byte_stream = $_[0];
362 wakaba 1.130
363 wakaba 1.131 my $onerror = $_[2] || sub {
364     my (%opt) = @_;
365     warn "Parse error ($opt{type})\n";
366     };
367     $self->{parse_error} = $onerror; # updated later by parse_char_string
368    
369 wakaba 1.158 my $get_wrapper = $_[3] || sub ($) {
370     return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
371     };
372    
373 wakaba 1.130 ## HTML5 encoding sniffing algorithm
374     require Message::Charset::Info;
375     my $charset;
376 wakaba 1.133 my $buffer;
377     my ($char_stream, $e_status);
378 wakaba 1.130
379     SNIFFING: {
380 wakaba 1.156 ## NOTE: By setting |allow_fallback| option true when the
381     ## |get_decode_handle| method is invoked, we ignore what the HTML5
382     ## spec requires, i.e. unsupported encoding should be ignored.
383     ## TODO: We should not do this unless the parser is invoked
384     ## in the conformance checking mode, in which this behavior
385     ## would be useful.
386 wakaba 1.130
387     ## Step 1
388     if (defined $charset_name) {
389 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
390     ## TODO: Is this ok? Transfer protocol's parameter should be
391     ## interpreted in its semantics?
392 wakaba 1.130
393     ## ISSUE: Unsupported encoding is not ignored according to the spec.
394 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
395     ($byte_stream, allow_error_reporting => 1,
396 wakaba 1.130 allow_fallback => 1);
397 wakaba 1.133 if ($char_stream) {
398 wakaba 1.130 $self->{confident} = 1;
399     last SNIFFING;
400 wakaba 1.133 } else {
401     ## TODO: unsupported error
402 wakaba 1.130 }
403     }
404    
405     ## Step 2
406 wakaba 1.133 my $byte_buffer = '';
407     for (1..1024) {
408     my $char = $byte_stream->getc;
409     last unless defined $char;
410     $byte_buffer .= $char;
411     } ## TODO: timeout
412 wakaba 1.130
413     ## Step 3
414 wakaba 1.133 if ($byte_buffer =~ /^\xFE\xFF/) {
415 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
416 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
417     ($byte_stream, allow_error_reporting => 1,
418     allow_fallback => 1, byte_buffer => \$byte_buffer);
419 wakaba 1.130 $self->{confident} = 1;
420     last SNIFFING;
421 wakaba 1.133 } elsif ($byte_buffer =~ /^\xFF\xFE/) {
422 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
423 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
424     ($byte_stream, allow_error_reporting => 1,
425     allow_fallback => 1, byte_buffer => \$byte_buffer);
426 wakaba 1.130 $self->{confident} = 1;
427     last SNIFFING;
428 wakaba 1.133 } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
429 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-8');
430 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
431     ($byte_stream, allow_error_reporting => 1,
432     allow_fallback => 1, byte_buffer => \$byte_buffer);
433 wakaba 1.130 $self->{confident} = 1;
434     last SNIFFING;
435     }
436    
437     ## Step 4
438     ## TODO: <meta charset>
439    
440     ## Step 5
441     ## TODO: from history
442    
443     ## Step 6
444 wakaba 1.67 require Whatpm::Charset::UniversalCharDet;
445 wakaba 1.130 $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
446 wakaba 1.133 ($byte_buffer);
447 wakaba 1.130 if (defined $charset_name) {
448 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
449 wakaba 1.130
450     ## ISSUE: Unsupported encoding is not ignored according to the spec.
451 wakaba 1.133 require Whatpm::Charset::DecodeHandle;
452     $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
453     ($byte_stream);
454     ($char_stream, $e_status) = $charset->get_decode_handle
455     ($buffer, allow_error_reporting => 1,
456     allow_fallback => 1, byte_buffer => \$byte_buffer);
457     if ($char_stream) {
458     $buffer->{buffer} = $byte_buffer;
459 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'sniffing:chardet',
460     text => $charset_name,
461     level => $self->{level}->{info},
462     layer => 'encode',
463 wakaba 1.131 line => 1, column => 1);
464 wakaba 1.130 $self->{confident} = 0;
465     last SNIFFING;
466     }
467     }
468    
469     ## Step 7: default
470     ## TODO: Make this configurable.
471 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
472 wakaba 1.130 ## NOTE: We choose |windows-1252| here, since |utf-8| should be
473     ## detectable in the step 6.
474 wakaba 1.133 require Whatpm::Charset::DecodeHandle;
475     $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
476     ($byte_stream);
477     ($char_stream, $e_status)
478     = $charset->get_decode_handle ($buffer,
479     allow_error_reporting => 1,
480     allow_fallback => 1,
481     byte_buffer => \$byte_buffer);
482     $buffer->{buffer} = $byte_buffer;
483 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'sniffing:default',
484     text => 'windows-1252',
485     level => $self->{level}->{info},
486     line => 1, column => 1,
487     layer => 'encode');
488 wakaba 1.65 $self->{confident} = 0;
489 wakaba 1.130 } # SNIFFING
490    
491     if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
492 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
493 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:fallback',
494 wakaba 1.156 #text => $self->{input_encoding},
495 wakaba 1.150 level => $self->{level}->{uncertain},
496     line => 1, column => 1,
497     layer => 'encode');
498 wakaba 1.130 } elsif (not ($e_status &
499 wakaba 1.174 Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
500 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name;
501 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:no error',
502     text => $self->{input_encoding},
503     level => $self->{level}->{uncertain},
504     line => 1, column => 1,
505     layer => 'encode');
506 wakaba 1.156 } else {
507     $self->{input_encoding} = $charset->get_iana_name;
508 wakaba 1.65 }
509    
510     $self->{change_encoding} = sub {
511     my $self = shift;
512 wakaba 1.131 $charset_name = shift;
513 wakaba 1.112 my $token = shift;
514 wakaba 1.65
515 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
516 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
517     ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
518     byte_buffer => \ $buffer->{buffer});
519 wakaba 1.131
520 wakaba 1.133 if ($char_stream) { # if supported
521 wakaba 1.131 ## "Change the encoding" algorithm:
522    
523     ## Step 1
524 wakaba 1.146 if ($charset->{category} &
525     Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
526 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-8');
527 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
528     ($byte_stream,
529     byte_buffer => \ $buffer->{buffer});
530 wakaba 1.131 }
531     $charset_name = $charset->get_iana_name;
532    
533     ## Step 2
534     if (defined $self->{input_encoding} and
535     $self->{input_encoding} eq $charset_name) {
536 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'charset label:matching',
537     text => $charset_name,
538     level => $self->{level}->{info});
539 wakaba 1.131 $self->{confident} = 1;
540     return;
541     }
542 wakaba 1.65
543 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'charset label detected',
544     text => $self->{input_encoding},
545     value => $charset_name,
546     level => $self->{level}->{warn},
547     token => $token);
548 wakaba 1.131
549     ## Step 3
550     # if (can) {
551     ## change the encoding on the fly.
552     #$self->{confident} = 1;
553     #return;
554     # }
555    
556     ## Step 4
557     throw Whatpm::HTML::RestartParser ();
558 wakaba 1.65 }
559     }; # $self->{change_encoding}
560    
561 wakaba 1.133 my $char_onerror = sub {
562     my (undef, $type, %opt) = @_;
563 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, layer => 'encode',
564 wakaba 1.170 line => $self->{line}, column => $self->{column} + 1,
565     %opt, type => $type);
566 wakaba 1.133 if ($opt{octets}) {
567     ${$opt{octets}} = "\x{FFFD}"; # relacement character
568     }
569     };
570 wakaba 1.158
571     my $wrapped_char_stream = $get_wrapper->($char_stream);
572     $wrapped_char_stream->onerror ($char_onerror);
573 wakaba 1.133
574 wakaba 1.178 my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
575 wakaba 1.65 my $return;
576     try {
577 wakaba 1.158 $return = $self->parse_char_stream ($wrapped_char_stream, @args);
578 wakaba 1.65 } catch Whatpm::HTML::RestartParser with {
579 wakaba 1.131 ## NOTE: Invoked after {change_encoding}.
580    
581     if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
582 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
583 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:fallback',
584     level => $self->{level}->{uncertain},
585 wakaba 1.156 #text => $self->{input_encoding},
586 wakaba 1.150 line => 1, column => 1,
587     layer => 'encode');
588 wakaba 1.131 } elsif (not ($e_status &
589 wakaba 1.174 Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
590 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name;
591 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:no error',
592     text => $self->{input_encoding},
593     level => $self->{level}->{uncertain},
594     line => 1, column => 1,
595     layer => 'encode');
596 wakaba 1.156 } else {
597     $self->{input_encoding} = $charset->get_iana_name;
598 wakaba 1.131 }
599 wakaba 1.65 $self->{confident} = 1;
600 wakaba 1.158
601     $wrapped_char_stream = $get_wrapper->($char_stream);
602     $wrapped_char_stream->onerror ($char_onerror);
603    
604     $return = $self->parse_char_stream ($wrapped_char_stream, @args);
605 wakaba 1.65 };
606     return $return;
607 wakaba 1.135 } # parse_byte_stream
608 wakaba 1.65
609 wakaba 1.71 ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
610     ## and the HTML layer MUST ignore it. However, we does strip BOM in
611     ## the encoding layer and the HTML layer does not ignore any U+FEFF,
612     ## because the core part of our HTML parser expects a string of character,
613     ## not a string of bytes or code units or anything which might contain a BOM.
614     ## Therefore, any parser interface that accepts a string of bytes,
615     ## such as |parse_byte_string| in this module, must ensure that it does
616     ## strip the BOM and never strip any ZWNBSP.
617    
618 wakaba 1.158 sub parse_char_string ($$$;$$) {
619     #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
620 wakaba 1.132 my $self = shift;
621 wakaba 1.136 my $s = ref $_[0] ? $_[0] : \($_[0]);
622 wakaba 1.167 require Whatpm::Charset::DecodeHandle;
623     my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
624 wakaba 1.132 return $self->parse_char_stream ($input, @_[1..$#_]);
625     } # parse_char_string
626 wakaba 1.158 *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
627 wakaba 1.65
628 wakaba 1.178 sub parse_char_stream ($$$;$$) {
629 wakaba 1.65 my $self = ref $_[0] ? shift : shift->new;
630 wakaba 1.132 my $input = $_[0];
631 wakaba 1.1 $self->{document} = $_[1];
632 wakaba 1.65 @{$self->{document}->child_nodes} = ();
633 wakaba 1.1
634 wakaba 1.3 ## NOTE: |set_inner_html| copies most of this method's code
635    
636 wakaba 1.65 $self->{confident} = 1 unless exists $self->{confident};
637 wakaba 1.66 $self->{document}->input_encoding ($self->{input_encoding})
638     if defined $self->{input_encoding};
639 wakaba 1.174 ## TODO: |{input_encoding}| is needless?
640 wakaba 1.65
641 wakaba 1.110 $self->{line_prev} = $self->{line} = 1;
642 wakaba 1.175 $self->{column_prev} = -1;
643     $self->{column} = 0;
644 wakaba 1.178 $self->{set_next_char} = sub {
645 wakaba 1.1 my $self = shift;
646 wakaba 1.13
647 wakaba 1.174 my $char = '';
648 wakaba 1.136 if (defined $self->{next_next_char}) {
649     $char = $self->{next_next_char};
650     delete $self->{next_next_char};
651 wakaba 1.174 $self->{next_char} = ord $char;
652 wakaba 1.136 } else {
653 wakaba 1.175 $self->{char_buffer} = '';
654     $self->{char_buffer_pos} = 0;
655    
656     my $count = $input->manakai_read_until
657 wakaba 1.178 ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
658 wakaba 1.175 if ($count) {
659     $self->{line_prev} = $self->{line};
660     $self->{column_prev} = $self->{column};
661     $self->{column}++;
662     $self->{next_char}
663     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
664     return;
665     }
666    
667 wakaba 1.174 if ($input->read ($char, 1)) {
668     $self->{next_char} = ord $char;
669     } else {
670     $self->{next_char} = -1;
671     return;
672     }
673 wakaba 1.136 }
674 wakaba 1.110
675     ($self->{line_prev}, $self->{column_prev})
676     = ($self->{line}, $self->{column});
677     $self->{column}++;
678 wakaba 1.1
679 wakaba 1.76 if ($self->{next_char} == 0x000A) { # LF
680 wakaba 1.129
681 wakaba 1.110 $self->{line}++;
682     $self->{column} = 0;
683 wakaba 1.76 } elsif ($self->{next_char} == 0x000D) { # CR
684 wakaba 1.129
685 wakaba 1.166 ## TODO: support for abort/streaming
686 wakaba 1.174 my $next = '';
687     if ($input->read ($next, 1) and $next ne "\x0A") {
688 wakaba 1.136 $self->{next_next_char} = $next;
689 wakaba 1.132 }
690 wakaba 1.76 $self->{next_char} = 0x000A; # LF # MUST
691 wakaba 1.110 $self->{line}++;
692     $self->{column} = 0;
693 wakaba 1.76 } elsif ($self->{next_char} == 0x0000) { # NULL
694 wakaba 1.129
695 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'NULL');
696 wakaba 1.76 $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
697 wakaba 1.1 }
698     };
699    
700 wakaba 1.168 $self->{read_until} = sub {
701     #my ($scalar, $specials_range, $offset) = @_;
702     return 0 if defined $self->{next_next_char};
703 wakaba 1.176
704 wakaba 1.178 my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
705 wakaba 1.176 my $offset = $_[2] || 0;
706    
707     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
708     pos ($self->{char_buffer}) = $self->{char_buffer_pos};
709     if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
710     substr ($_[0], $offset)
711     = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
712     my $count = $+[0] - $-[0];
713     if ($count) {
714     $self->{column} += $count;
715     $self->{char_buffer_pos} += $count;
716     $self->{line_prev} = $self->{line};
717     $self->{column_prev} = $self->{column} - 1;
718     $self->{prev_char} = [-1, -1, -1];
719     $self->{next_char} = -1;
720     }
721     return $count;
722     } else {
723     return 0;
724     }
725     } else {
726     my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
727     if ($count) {
728     $self->{column} += $count;
729     $self->{line_prev} = $self->{line};
730     $self->{column_prev} = $self->{column} - 1;
731     $self->{prev_char} = [-1, -1, -1];
732     $self->{next_char} = -1;
733     }
734     return $count;
735 wakaba 1.168 }
736     }; # $self->{read_until}
737 wakaba 1.167
738 wakaba 1.3 my $onerror = $_[2] || sub {
739     my (%opt) = @_;
740 wakaba 1.110 my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
741     my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
742     warn "Parse error ($opt{type}) at line $line column $column\n";
743 wakaba 1.3 };
744     $self->{parse_error} = sub {
745 wakaba 1.110 $onerror->(line => $self->{line}, column => $self->{column}, @_);
746 wakaba 1.1 };
747    
748 wakaba 1.178 my $char_onerror = sub {
749     my (undef, $type, %opt) = @_;
750     $self->{parse_error}->(level => $self->{level}->{must}, layer => 'encode',
751     line => $self->{line}, column => $self->{column} + 1,
752     %opt, type => $type);
753     }; # $char_onerror
754    
755     if ($_[3]) {
756     $input = $_[3]->($input);
757     $input->onerror ($char_onerror);
758     } else {
759     $input->onerror ($char_onerror) unless defined $input->onerror;
760     }
761    
762 wakaba 1.1 $self->_initialize_tokenizer;
763     $self->_initialize_tree_constructor;
764     $self->_construct_tree;
765     $self->_terminate_tree_constructor;
766    
767 wakaba 1.110 delete $self->{parse_error}; # remove loop
768    
769 wakaba 1.1 return $self->{document};
770 wakaba 1.132 } # parse_char_stream
771 wakaba 1.1
772     sub new ($) {
773     my $class = shift;
774 wakaba 1.131 my $self = bless {
775 wakaba 1.150 level => {must => 'm',
776 wakaba 1.155 should => 's',
777 wakaba 1.150 warn => 'w',
778     info => 'i',
779     uncertain => 'u'},
780 wakaba 1.131 }, $class;
781 wakaba 1.76 $self->{set_next_char} = sub {
782     $self->{next_char} = -1;
783 wakaba 1.1 };
784     $self->{parse_error} = sub {
785     #
786     };
787 wakaba 1.65 $self->{change_encoding} = sub {
788     # if ($_[0] is a supported encoding) {
789     # run "change the encoding" algorithm;
790     # throw Whatpm::HTML::RestartParser (charset => $new_encoding);
791     # }
792     };
793 wakaba 1.63 $self->{application_cache_selection} = sub {
794     #
795     };
796 wakaba 1.1 return $self;
797     } # new
798    
799 wakaba 1.41 sub CM_ENTITY () { 0b001 } # & markup in data
800     sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
801     sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
802    
803     sub PLAINTEXT_CONTENT_MODEL () { 0 }
804     sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
805     sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
806     sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
807    
808 wakaba 1.59 sub DATA_STATE () { 0 }
809 wakaba 1.164 #sub ENTITY_DATA_STATE () { 1 }
810 wakaba 1.59 sub TAG_OPEN_STATE () { 2 }
811     sub CLOSE_TAG_OPEN_STATE () { 3 }
812     sub TAG_NAME_STATE () { 4 }
813     sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
814     sub ATTRIBUTE_NAME_STATE () { 6 }
815     sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
816     sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
817     sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
818     sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
819     sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
820 wakaba 1.164 #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
821 wakaba 1.59 sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
822     sub COMMENT_START_STATE () { 14 }
823     sub COMMENT_START_DASH_STATE () { 15 }
824     sub COMMENT_STATE () { 16 }
825     sub COMMENT_END_STATE () { 17 }
826     sub COMMENT_END_DASH_STATE () { 18 }
827     sub BOGUS_COMMENT_STATE () { 19 }
828     sub DOCTYPE_STATE () { 20 }
829     sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
830     sub DOCTYPE_NAME_STATE () { 22 }
831     sub AFTER_DOCTYPE_NAME_STATE () { 23 }
832     sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
833     sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
834     sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
835     sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
836     sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
837     sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
838     sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
839     sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
840     sub BOGUS_DOCTYPE_STATE () { 32 }
841 wakaba 1.72 sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
842 wakaba 1.122 sub SELF_CLOSING_START_TAG_STATE () { 34 }
843 wakaba 1.161 sub CDATA_SECTION_STATE () { 35 }
844 wakaba 1.160 sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
845     sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
846     sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
847     sub CDATA_PCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
848 wakaba 1.161 sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
849     sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
850 wakaba 1.162 sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
851     sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
852 wakaba 1.165 ## NOTE: "Entity data state", "entity in attribute value state", and
853     ## "consume a character reference" algorithm are jointly implemented
854     ## using the following six states:
855     sub ENTITY_STATE () { 44 }
856     sub ENTITY_HASH_STATE () { 45 }
857     sub NCR_NUM_STATE () { 46 }
858     sub HEXREF_X_STATE () { 47 }
859     sub HEXREF_HEX_STATE () { 48 }
860     sub ENTITY_NAME_STATE () { 49 }
861 wakaba 1.59
862 wakaba 1.57 sub DOCTYPE_TOKEN () { 1 }
863     sub COMMENT_TOKEN () { 2 }
864     sub START_TAG_TOKEN () { 3 }
865     sub END_TAG_TOKEN () { 4 }
866     sub END_OF_FILE_TOKEN () { 5 }
867     sub CHARACTER_TOKEN () { 6 }
868    
869 wakaba 1.56 sub AFTER_HTML_IMS () { 0b100 }
870     sub HEAD_IMS () { 0b1000 }
871     sub BODY_IMS () { 0b10000 }
872 wakaba 1.58 sub BODY_TABLE_IMS () { 0b100000 }
873 wakaba 1.56 sub TABLE_IMS () { 0b1000000 }
874 wakaba 1.58 sub ROW_IMS () { 0b10000000 }
875 wakaba 1.56 sub BODY_AFTER_IMS () { 0b100000000 }
876     sub FRAME_IMS () { 0b1000000000 }
877 wakaba 1.101 sub SELECT_IMS () { 0b10000000000 }
878 wakaba 1.123 sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
879     ## NOTE: "in foreign content" insertion mode is special; it is combined
880     ## with the secondary insertion mode. In this parser, they are stored
881     ## together in the bit-or'ed form.
882 wakaba 1.56
883 wakaba 1.85 ## NOTE: "initial" and "before html" insertion modes have no constants.
884 wakaba 1.84
885     ## NOTE: "after after body" insertion mode.
886 wakaba 1.56 sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
887 wakaba 1.84
888     ## NOTE: "after after frameset" insertion mode.
889 wakaba 1.56 sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
890 wakaba 1.84
891 wakaba 1.56 sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
892     sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
893     sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
894     sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
895     sub IN_BODY_IM () { BODY_IMS }
896 wakaba 1.58 sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
897     sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
898     sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
899     sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
900 wakaba 1.56 sub IN_TABLE_IM () { TABLE_IMS }
901     sub AFTER_BODY_IM () { BODY_AFTER_IMS }
902     sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
903     sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
904 wakaba 1.101 sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
905     sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
906 wakaba 1.56 sub IN_COLUMN_GROUP_IM () { 0b10 }
907    
908 wakaba 1.1 ## Implementations MUST act as if state machine in the spec
909    
910     sub _initialize_tokenizer ($) {
911     my $self = shift;
912 wakaba 1.59 $self->{state} = DATA_STATE; # MUST
913 wakaba 1.159 #$self->{state_keyword}; # initialized when used
914 wakaba 1.165 #$self->{entity__value}; # initialized when used
915     #$self->{entity__match}; # initialized when used
916 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # be
917 wakaba 1.161 undef $self->{current_token};
918 wakaba 1.1 undef $self->{current_attribute};
919     undef $self->{last_emitted_start_tag_name};
920 wakaba 1.165 #$self->{prev_state}; # initialized when used
921 wakaba 1.122 delete $self->{self_closing};
922 wakaba 1.175 $self->{char_buffer} = '';
923     $self->{char_buffer_pos} = 0;
924 wakaba 1.176 $self->{prev_char} = [-1, -1, -1];
925     $self->{next_char} = -1;
926 wakaba 1.1
927 wakaba 1.175 pop @{$self->{prev_char}};
928     unshift @{$self->{prev_char}}, $self->{next_char};
929    
930     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
931     $self->{line_prev} = $self->{line};
932     $self->{column_prev} = $self->{column};
933     $self->{column}++;
934     $self->{next_char}
935     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
936     } else {
937     $self->{set_next_char}->($self);
938     }
939 wakaba 1.1
940     $self->{token} = [];
941 wakaba 1.18 # $self->{escape}
942 wakaba 1.1 } # _initialize_tokenizer
943    
944     ## A token has:
945 wakaba 1.57 ## ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
946     ## CHARACTER_TOKEN, or END_OF_FILE_TOKEN
947     ## ->{name} (DOCTYPE_TOKEN)
948     ## ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
949     ## ->{public_identifier} (DOCTYPE_TOKEN)
950     ## ->{system_identifier} (DOCTYPE_TOKEN)
951 wakaba 1.75 ## ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
952 wakaba 1.57 ## ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
953 wakaba 1.68 ## ->{name}
954     ## ->{value}
955     ## ->{has_reference} == 1 or 0
956 wakaba 1.57 ## ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
957 wakaba 1.122 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
958     ## |->{self_closing}| is used to save the value of |$self->{self_closing}|
959     ## while the token is pushed back to the stack.
960    
961 wakaba 1.1 ## Emitted token MUST immediately be handled by the tree construction state.
962    
963     ## Before each step, UA MAY check to see if either one of the scripts in
964     ## "list of scripts that will execute as soon as possible" or the first
965     ## script in the "list of scripts that will execute asynchronously",
966     ## has completed loading. If one has, then it MUST be executed
967     ## and removed from the list.
968    
969 wakaba 1.165 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
970     ## (This requirement was dropped from HTML5 spec, unfortunately.)
971 wakaba 1.61
972 wakaba 1.1 sub _get_next_token ($) {
973     my $self = shift;
974 wakaba 1.122
975     if ($self->{self_closing}) {
976 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc', token => $self->{current_token});
977 wakaba 1.122 ## NOTE: The |self_closing| flag is only set by start tag token.
978     ## In addition, when a start tag token is emitted, it is always set to
979     ## |current_token|.
980     delete $self->{self_closing};
981     }
982    
983 wakaba 1.1 if (@{$self->{token}}) {
984 wakaba 1.122 $self->{self_closing} = $self->{token}->[0]->{self_closing};
985 wakaba 1.1 return shift @{$self->{token}};
986     }
987    
988     A: {
989 wakaba 1.59 if ($self->{state} == DATA_STATE) {
990 wakaba 1.76 if ($self->{next_char} == 0x0026) { # &
991 wakaba 1.72 if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
992     not $self->{escape}) {
993 wakaba 1.77
994 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
995     ## "entity data state". In this implementation, the tokenizer
996     ## is switched to the |ENTITY_STATE|, which is an implementation
997     ## of the "consume a character reference" algorithm.
998     $self->{entity_additional} = -1;
999 wakaba 1.165 $self->{prev_state} = DATA_STATE;
1000 wakaba 1.163 $self->{state} = ENTITY_STATE;
1001 wakaba 1.1
1002 wakaba 1.175 pop @{$self->{prev_char}};
1003     unshift @{$self->{prev_char}}, $self->{next_char};
1004    
1005     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1006     $self->{line_prev} = $self->{line};
1007     $self->{column_prev} = $self->{column};
1008     $self->{column}++;
1009     $self->{next_char}
1010     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1011     } else {
1012     $self->{set_next_char}->($self);
1013     }
1014 wakaba 1.1
1015     redo A;
1016     } else {
1017 wakaba 1.77
1018 wakaba 1.1 #
1019     }
1020 wakaba 1.76 } elsif ($self->{next_char} == 0x002D) { # -
1021 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1022 wakaba 1.13 unless ($self->{escape}) {
1023 wakaba 1.76 if ($self->{prev_char}->[0] == 0x002D and # -
1024     $self->{prev_char}->[1] == 0x0021 and # !
1025     $self->{prev_char}->[2] == 0x003C) { # <
1026 wakaba 1.77
1027 wakaba 1.13 $self->{escape} = 1;
1028 wakaba 1.77 } else {
1029    
1030 wakaba 1.13 }
1031 wakaba 1.77 } else {
1032    
1033 wakaba 1.13 }
1034     }
1035    
1036     #
1037 wakaba 1.76 } elsif ($self->{next_char} == 0x003C) { # <
1038 wakaba 1.41 if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1039     (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1040 wakaba 1.13 not $self->{escape})) {
1041 wakaba 1.77
1042 wakaba 1.59 $self->{state} = TAG_OPEN_STATE;
1043 wakaba 1.1
1044 wakaba 1.175 pop @{$self->{prev_char}};
1045     unshift @{$self->{prev_char}}, $self->{next_char};
1046    
1047     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1048     $self->{line_prev} = $self->{line};
1049     $self->{column_prev} = $self->{column};
1050     $self->{column}++;
1051     $self->{next_char}
1052     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1053     } else {
1054     $self->{set_next_char}->($self);
1055     }
1056 wakaba 1.1
1057     redo A;
1058     } else {
1059 wakaba 1.77
1060 wakaba 1.1 #
1061     }
1062 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1063 wakaba 1.13 if ($self->{escape} and
1064 wakaba 1.41 ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1065 wakaba 1.76 if ($self->{prev_char}->[0] == 0x002D and # -
1066     $self->{prev_char}->[1] == 0x002D) { # -
1067 wakaba 1.77
1068 wakaba 1.13 delete $self->{escape};
1069 wakaba 1.77 } else {
1070    
1071 wakaba 1.13 }
1072 wakaba 1.77 } else {
1073    
1074 wakaba 1.13 }
1075    
1076     #
1077 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1078 wakaba 1.77
1079 wakaba 1.110 return ({type => END_OF_FILE_TOKEN,
1080     line => $self->{line}, column => $self->{column}});
1081 wakaba 1.1 last A; ## TODO: ok?
1082 wakaba 1.77 } else {
1083    
1084 wakaba 1.1 }
1085     # Anything else
1086 wakaba 1.57 my $token = {type => CHARACTER_TOKEN,
1087 wakaba 1.110 data => chr $self->{next_char},
1088 wakaba 1.118 line => $self->{line}, column => $self->{column},
1089 wakaba 1.116 };
1090 wakaba 1.168 $self->{read_until}->($token->{data}, q[-!<>&], length $token->{data});
1091 wakaba 1.167
1092 wakaba 1.1 ## Stay in the data state
1093    
1094 wakaba 1.175 pop @{$self->{prev_char}};
1095     unshift @{$self->{prev_char}}, $self->{next_char};
1096    
1097     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1098     $self->{line_prev} = $self->{line};
1099     $self->{column_prev} = $self->{column};
1100     $self->{column}++;
1101     $self->{next_char}
1102     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1103     } else {
1104     $self->{set_next_char}->($self);
1105     }
1106 wakaba 1.1
1107    
1108     return ($token);
1109    
1110     redo A;
1111 wakaba 1.59 } elsif ($self->{state} == TAG_OPEN_STATE) {
1112 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1113 wakaba 1.76 if ($self->{next_char} == 0x002F) { # /
1114 wakaba 1.1
1115 wakaba 1.77
1116 wakaba 1.175 pop @{$self->{prev_char}};
1117     unshift @{$self->{prev_char}}, $self->{next_char};
1118    
1119     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1120     $self->{line_prev} = $self->{line};
1121     $self->{column_prev} = $self->{column};
1122     $self->{column}++;
1123     $self->{next_char}
1124     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1125     } else {
1126     $self->{set_next_char}->($self);
1127     }
1128 wakaba 1.1
1129 wakaba 1.59 $self->{state} = CLOSE_TAG_OPEN_STATE;
1130 wakaba 1.1 redo A;
1131     } else {
1132 wakaba 1.77
1133 wakaba 1.1 ## reconsume
1134 wakaba 1.59 $self->{state} = DATA_STATE;
1135 wakaba 1.1
1136 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '<',
1137 wakaba 1.118 line => $self->{line_prev},
1138     column => $self->{column_prev},
1139 wakaba 1.116 });
1140 wakaba 1.1
1141     redo A;
1142     }
1143 wakaba 1.41 } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1144 wakaba 1.76 if ($self->{next_char} == 0x0021) { # !
1145 wakaba 1.77
1146 wakaba 1.59 $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1147 wakaba 1.1
1148 wakaba 1.175 pop @{$self->{prev_char}};
1149     unshift @{$self->{prev_char}}, $self->{next_char};
1150    
1151     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1152     $self->{line_prev} = $self->{line};
1153     $self->{column_prev} = $self->{column};
1154     $self->{column}++;
1155     $self->{next_char}
1156     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1157     } else {
1158     $self->{set_next_char}->($self);
1159     }
1160 wakaba 1.1
1161     redo A;
1162 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
1163 wakaba 1.77
1164 wakaba 1.59 $self->{state} = CLOSE_TAG_OPEN_STATE;
1165 wakaba 1.1
1166 wakaba 1.175 pop @{$self->{prev_char}};
1167     unshift @{$self->{prev_char}}, $self->{next_char};
1168    
1169     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1170     $self->{line_prev} = $self->{line};
1171     $self->{column_prev} = $self->{column};
1172     $self->{column}++;
1173     $self->{next_char}
1174     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1175     } else {
1176     $self->{set_next_char}->($self);
1177     }
1178 wakaba 1.1
1179     redo A;
1180 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
1181     $self->{next_char} <= 0x005A) { # A..Z
1182 wakaba 1.77
1183 wakaba 1.1 $self->{current_token}
1184 wakaba 1.57 = {type => START_TAG_TOKEN,
1185 wakaba 1.110 tag_name => chr ($self->{next_char} + 0x0020),
1186     line => $self->{line_prev},
1187     column => $self->{column_prev}};
1188 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1189 wakaba 1.1
1190 wakaba 1.175 pop @{$self->{prev_char}};
1191     unshift @{$self->{prev_char}}, $self->{next_char};
1192    
1193     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1194     $self->{line_prev} = $self->{line};
1195     $self->{column_prev} = $self->{column};
1196     $self->{column}++;
1197     $self->{next_char}
1198     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1199     } else {
1200     $self->{set_next_char}->($self);
1201     }
1202 wakaba 1.1
1203     redo A;
1204 wakaba 1.76 } elsif (0x0061 <= $self->{next_char} and
1205     $self->{next_char} <= 0x007A) { # a..z
1206 wakaba 1.77
1207 wakaba 1.57 $self->{current_token} = {type => START_TAG_TOKEN,
1208 wakaba 1.110 tag_name => chr ($self->{next_char}),
1209     line => $self->{line_prev},
1210     column => $self->{column_prev}};
1211 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1212 wakaba 1.1
1213 wakaba 1.175 pop @{$self->{prev_char}};
1214     unshift @{$self->{prev_char}}, $self->{next_char};
1215    
1216     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1217     $self->{line_prev} = $self->{line};
1218     $self->{column_prev} = $self->{column};
1219     $self->{column}++;
1220     $self->{next_char}
1221     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1222     } else {
1223     $self->{set_next_char}->($self);
1224     }
1225 wakaba 1.1
1226     redo A;
1227 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1228 wakaba 1.77
1229 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty start tag',
1230 wakaba 1.113 line => $self->{line_prev},
1231     column => $self->{column_prev});
1232 wakaba 1.59 $self->{state} = DATA_STATE;
1233 wakaba 1.1
1234 wakaba 1.175 pop @{$self->{prev_char}};
1235     unshift @{$self->{prev_char}}, $self->{next_char};
1236    
1237     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1238     $self->{line_prev} = $self->{line};
1239     $self->{column_prev} = $self->{column};
1240     $self->{column}++;
1241     $self->{next_char}
1242     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1243     } else {
1244     $self->{set_next_char}->($self);
1245     }
1246 wakaba 1.1
1247    
1248 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '<>',
1249 wakaba 1.118 line => $self->{line_prev},
1250     column => $self->{column_prev},
1251 wakaba 1.116 });
1252 wakaba 1.1
1253     redo A;
1254 wakaba 1.76 } elsif ($self->{next_char} == 0x003F) { # ?
1255 wakaba 1.77
1256 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'pio',
1257 wakaba 1.113 line => $self->{line_prev},
1258     column => $self->{column_prev});
1259 wakaba 1.59 $self->{state} = BOGUS_COMMENT_STATE;
1260 wakaba 1.110 $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1261 wakaba 1.118 line => $self->{line_prev},
1262     column => $self->{column_prev},
1263 wakaba 1.116 };
1264 wakaba 1.76 ## $self->{next_char} is intentionally left as is
1265 wakaba 1.1 redo A;
1266     } else {
1267 wakaba 1.77
1268 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare stago',
1269 wakaba 1.133 line => $self->{line_prev},
1270     column => $self->{column_prev});
1271 wakaba 1.59 $self->{state} = DATA_STATE;
1272 wakaba 1.1 ## reconsume
1273    
1274 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '<',
1275 wakaba 1.118 line => $self->{line_prev},
1276     column => $self->{column_prev},
1277 wakaba 1.116 });
1278 wakaba 1.1
1279     redo A;
1280     }
1281     } else {
1282 wakaba 1.41 die "$0: $self->{content_model} in tag open";
1283 wakaba 1.1 }
1284 wakaba 1.59 } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1285 wakaba 1.160 ## NOTE: The "close tag open state" in the spec is implemented as
1286     ## |CLOSE_TAG_OPEN_STATE| and |CDATA_PCDATA_CLOSE_TAG_STATE|.
1287    
1288 wakaba 1.112 my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1289 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1290 wakaba 1.23 if (defined $self->{last_emitted_start_tag_name}) {
1291 wakaba 1.160 $self->{state} = CDATA_PCDATA_CLOSE_TAG_STATE;
1292     $self->{state_keyword} = '';
1293     ## Reconsume.
1294     redo A;
1295 wakaba 1.23 } else {
1296     ## No start tag token has ever been emitted
1297 wakaba 1.161 ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1298 wakaba 1.77
1299 wakaba 1.59 $self->{state} = DATA_STATE;
1300 wakaba 1.161 ## Reconsume.
1301 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '</',
1302 wakaba 1.118 line => $l, column => $c,
1303 wakaba 1.116 });
1304 wakaba 1.1 redo A;
1305     }
1306     }
1307 wakaba 1.160
1308 wakaba 1.76 if (0x0041 <= $self->{next_char} and
1309     $self->{next_char} <= 0x005A) { # A..Z
1310 wakaba 1.77
1311 wakaba 1.110 $self->{current_token}
1312     = {type => END_TAG_TOKEN,
1313     tag_name => chr ($self->{next_char} + 0x0020),
1314     line => $l, column => $c};
1315 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1316 wakaba 1.1
1317 wakaba 1.175 pop @{$self->{prev_char}};
1318     unshift @{$self->{prev_char}}, $self->{next_char};
1319    
1320     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1321     $self->{line_prev} = $self->{line};
1322     $self->{column_prev} = $self->{column};
1323     $self->{column}++;
1324     $self->{next_char}
1325     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1326     } else {
1327     $self->{set_next_char}->($self);
1328     }
1329 wakaba 1.1
1330     redo A;
1331 wakaba 1.76 } elsif (0x0061 <= $self->{next_char} and
1332     $self->{next_char} <= 0x007A) { # a..z
1333 wakaba 1.77
1334 wakaba 1.57 $self->{current_token} = {type => END_TAG_TOKEN,
1335 wakaba 1.110 tag_name => chr ($self->{next_char}),
1336     line => $l, column => $c};
1337 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1338 wakaba 1.1
1339 wakaba 1.175 pop @{$self->{prev_char}};
1340     unshift @{$self->{prev_char}}, $self->{next_char};
1341    
1342     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1343     $self->{line_prev} = $self->{line};
1344     $self->{column_prev} = $self->{column};
1345     $self->{column}++;
1346     $self->{next_char}
1347     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1348     } else {
1349     $self->{set_next_char}->($self);
1350     }
1351 wakaba 1.1
1352     redo A;
1353 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1354 wakaba 1.77
1355 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty end tag',
1356 wakaba 1.113 line => $self->{line_prev}, ## "<" in "</>"
1357     column => $self->{column_prev} - 1);
1358 wakaba 1.59 $self->{state} = DATA_STATE;
1359 wakaba 1.1
1360 wakaba 1.175 pop @{$self->{prev_char}};
1361     unshift @{$self->{prev_char}}, $self->{next_char};
1362    
1363     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1364     $self->{line_prev} = $self->{line};
1365     $self->{column_prev} = $self->{column};
1366     $self->{column}++;
1367     $self->{next_char}
1368     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1369     } else {
1370     $self->{set_next_char}->($self);
1371     }
1372 wakaba 1.1
1373     redo A;
1374 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1375 wakaba 1.77
1376 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare etago');
1377 wakaba 1.59 $self->{state} = DATA_STATE;
1378 wakaba 1.1 # reconsume
1379    
1380 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '</',
1381 wakaba 1.118 line => $l, column => $c,
1382 wakaba 1.116 });
1383 wakaba 1.1
1384     redo A;
1385     } else {
1386 wakaba 1.77
1387 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus end tag');
1388 wakaba 1.59 $self->{state} = BOGUS_COMMENT_STATE;
1389 wakaba 1.110 $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1390 wakaba 1.118 line => $self->{line_prev}, # "<" of "</"
1391     column => $self->{column_prev} - 1,
1392 wakaba 1.116 };
1393 wakaba 1.160 ## NOTE: $self->{next_char} is intentionally left as is.
1394     ## Although the "anything else" case of the spec not explicitly
1395     ## states that the next input character is to be reconsumed,
1396     ## it will be included to the |data| of the comment token
1397     ## generated from the bogus end tag, as defined in the
1398     ## "bogus comment state" entry.
1399     redo A;
1400     }
1401     } elsif ($self->{state} == CDATA_PCDATA_CLOSE_TAG_STATE) {
1402     my $ch = substr $self->{last_emitted_start_tag_name}, length $self->{state_keyword}, 1;
1403     if (length $ch) {
1404     my $CH = $ch;
1405     $ch =~ tr/a-z/A-Z/;
1406     my $nch = chr $self->{next_char};
1407     if ($nch eq $ch or $nch eq $CH) {
1408    
1409     ## Stay in the state.
1410     $self->{state_keyword} .= $nch;
1411    
1412 wakaba 1.175 pop @{$self->{prev_char}};
1413     unshift @{$self->{prev_char}}, $self->{next_char};
1414    
1415     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1416     $self->{line_prev} = $self->{line};
1417     $self->{column_prev} = $self->{column};
1418     $self->{column}++;
1419     $self->{next_char}
1420     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1421     } else {
1422     $self->{set_next_char}->($self);
1423     }
1424 wakaba 1.160
1425     redo A;
1426     } else {
1427    
1428     $self->{state} = DATA_STATE;
1429     ## Reconsume.
1430     return ({type => CHARACTER_TOKEN,
1431     data => '</' . $self->{state_keyword},
1432     line => $self->{line_prev},
1433     column => $self->{column_prev} - 1 - length $self->{state_keyword},
1434     });
1435     redo A;
1436     }
1437     } else { # after "<{tag-name}"
1438     unless ({
1439     0x0009 => 1, # HT
1440     0x000A => 1, # LF
1441     0x000B => 1, # VT
1442     0x000C => 1, # FF
1443     0x0020 => 1, # SP
1444     0x003E => 1, # >
1445     0x002F => 1, # /
1446     -1 => 1, # EOF
1447     }->{$self->{next_char}}) {
1448    
1449     ## Reconsume.
1450     $self->{state} = DATA_STATE;
1451     return ({type => CHARACTER_TOKEN,
1452     data => '</' . $self->{state_keyword},
1453     line => $self->{line_prev},
1454     column => $self->{column_prev} - 1 - length $self->{state_keyword},
1455     });
1456     redo A;
1457     } else {
1458    
1459     $self->{current_token}
1460     = {type => END_TAG_TOKEN,
1461     tag_name => $self->{last_emitted_start_tag_name},
1462     line => $self->{line_prev},
1463     column => $self->{column_prev} - 1 - length $self->{state_keyword}};
1464     $self->{state} = TAG_NAME_STATE;
1465     ## Reconsume.
1466     redo A;
1467     }
1468 wakaba 1.1 }
1469 wakaba 1.59 } elsif ($self->{state} == TAG_NAME_STATE) {
1470 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1471     $self->{next_char} == 0x000A or # LF
1472     $self->{next_char} == 0x000B or # VT
1473     $self->{next_char} == 0x000C or # FF
1474     $self->{next_char} == 0x0020) { # SP
1475 wakaba 1.77
1476 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1477 wakaba 1.1
1478 wakaba 1.175 pop @{$self->{prev_char}};
1479     unshift @{$self->{prev_char}}, $self->{next_char};
1480    
1481     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1482     $self->{line_prev} = $self->{line};
1483     $self->{column_prev} = $self->{column};
1484     $self->{column}++;
1485     $self->{next_char}
1486     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1487     } else {
1488     $self->{set_next_char}->($self);
1489     }
1490 wakaba 1.1
1491     redo A;
1492 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1493 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1494 wakaba 1.77
1495 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1496 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1497 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1498 wakaba 1.78 #if ($self->{current_token}->{attributes}) {
1499     # ## NOTE: This should never be reached.
1500     # !!! cp (36);
1501     # !!! parse-error (type => 'end tag attribute');
1502     #} else {
1503 wakaba 1.77
1504 wakaba 1.78 #}
1505 wakaba 1.1 } else {
1506     die "$0: $self->{current_token}->{type}: Unknown token type";
1507     }
1508 wakaba 1.59 $self->{state} = DATA_STATE;
1509 wakaba 1.1
1510 wakaba 1.175 pop @{$self->{prev_char}};
1511     unshift @{$self->{prev_char}}, $self->{next_char};
1512    
1513     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1514     $self->{line_prev} = $self->{line};
1515     $self->{column_prev} = $self->{column};
1516     $self->{column}++;
1517     $self->{next_char}
1518     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1519     } else {
1520     $self->{set_next_char}->($self);
1521     }
1522 wakaba 1.1
1523    
1524     return ($self->{current_token}); # start tag or end tag
1525    
1526     redo A;
1527 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
1528     $self->{next_char} <= 0x005A) { # A..Z
1529 wakaba 1.77
1530 wakaba 1.76 $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);
1531 wakaba 1.1 # start tag or end tag
1532     ## Stay in this state
1533    
1534 wakaba 1.175 pop @{$self->{prev_char}};
1535     unshift @{$self->{prev_char}}, $self->{next_char};
1536    
1537     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1538     $self->{line_prev} = $self->{line};
1539     $self->{column_prev} = $self->{column};
1540     $self->{column}++;
1541     $self->{next_char}
1542     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1543     } else {
1544     $self->{set_next_char}->($self);
1545     }
1546 wakaba 1.1
1547     redo A;
1548 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1549 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1550 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1551 wakaba 1.77
1552 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1553 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1554 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1555 wakaba 1.78 #if ($self->{current_token}->{attributes}) {
1556     # ## NOTE: This state should never be reached.
1557     # !!! cp (40);
1558     # !!! parse-error (type => 'end tag attribute');
1559     #} else {
1560 wakaba 1.77
1561 wakaba 1.78 #}
1562 wakaba 1.1 } else {
1563     die "$0: $self->{current_token}->{type}: Unknown token type";
1564     }
1565 wakaba 1.59 $self->{state} = DATA_STATE;
1566 wakaba 1.1 # reconsume
1567    
1568     return ($self->{current_token}); # start tag or end tag
1569    
1570     redo A;
1571 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
1572 wakaba 1.1
1573 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1574    
1575 wakaba 1.175 pop @{$self->{prev_char}};
1576     unshift @{$self->{prev_char}}, $self->{next_char};
1577    
1578     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1579     $self->{line_prev} = $self->{line};
1580     $self->{column_prev} = $self->{column};
1581     $self->{column}++;
1582     $self->{next_char}
1583     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1584     } else {
1585     $self->{set_next_char}->($self);
1586     }
1587 wakaba 1.1
1588     redo A;
1589     } else {
1590 wakaba 1.77
1591 wakaba 1.76 $self->{current_token}->{tag_name} .= chr $self->{next_char};
1592 wakaba 1.1 # start tag or end tag
1593     ## Stay in the state
1594    
1595 wakaba 1.175 pop @{$self->{prev_char}};
1596     unshift @{$self->{prev_char}}, $self->{next_char};
1597    
1598     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1599     $self->{line_prev} = $self->{line};
1600     $self->{column_prev} = $self->{column};
1601     $self->{column}++;
1602     $self->{next_char}
1603     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1604     } else {
1605     $self->{set_next_char}->($self);
1606     }
1607 wakaba 1.1
1608     redo A;
1609     }
1610 wakaba 1.59 } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1611 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1612     $self->{next_char} == 0x000A or # LF
1613     $self->{next_char} == 0x000B or # VT
1614     $self->{next_char} == 0x000C or # FF
1615     $self->{next_char} == 0x0020) { # SP
1616 wakaba 1.77
1617 wakaba 1.1 ## Stay in the state
1618    
1619 wakaba 1.175 pop @{$self->{prev_char}};
1620     unshift @{$self->{prev_char}}, $self->{next_char};
1621    
1622     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1623     $self->{line_prev} = $self->{line};
1624     $self->{column_prev} = $self->{column};
1625     $self->{column}++;
1626     $self->{next_char}
1627     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1628     } else {
1629     $self->{set_next_char}->($self);
1630     }
1631 wakaba 1.1
1632     redo A;
1633 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1634 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1635 wakaba 1.77
1636 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1637 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1638 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1639 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1640 wakaba 1.77
1641 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1642 wakaba 1.77 } else {
1643    
1644 wakaba 1.1 }
1645     } else {
1646     die "$0: $self->{current_token}->{type}: Unknown token type";
1647     }
1648 wakaba 1.59 $self->{state} = DATA_STATE;
1649 wakaba 1.1
1650 wakaba 1.175 pop @{$self->{prev_char}};
1651     unshift @{$self->{prev_char}}, $self->{next_char};
1652    
1653     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1654     $self->{line_prev} = $self->{line};
1655     $self->{column_prev} = $self->{column};
1656     $self->{column}++;
1657     $self->{next_char}
1658     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1659     } else {
1660     $self->{set_next_char}->($self);
1661     }
1662 wakaba 1.1
1663    
1664     return ($self->{current_token}); # start tag or end tag
1665    
1666     redo A;
1667 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
1668     $self->{next_char} <= 0x005A) { # A..Z
1669 wakaba 1.77
1670 wakaba 1.117 $self->{current_attribute}
1671     = {name => chr ($self->{next_char} + 0x0020),
1672     value => '',
1673     line => $self->{line}, column => $self->{column}};
1674 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
1675 wakaba 1.1
1676 wakaba 1.175 pop @{$self->{prev_char}};
1677     unshift @{$self->{prev_char}}, $self->{next_char};
1678    
1679     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1680     $self->{line_prev} = $self->{line};
1681     $self->{column_prev} = $self->{column};
1682     $self->{column}++;
1683     $self->{next_char}
1684     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1685     } else {
1686     $self->{set_next_char}->($self);
1687     }
1688 wakaba 1.1
1689     redo A;
1690 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
1691 wakaba 1.1
1692 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1693    
1694 wakaba 1.175 pop @{$self->{prev_char}};
1695     unshift @{$self->{prev_char}}, $self->{next_char};
1696    
1697     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1698     $self->{line_prev} = $self->{line};
1699     $self->{column_prev} = $self->{column};
1700     $self->{column}++;
1701     $self->{next_char}
1702     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1703     } else {
1704     $self->{set_next_char}->($self);
1705     }
1706 wakaba 1.1
1707     redo A;
1708 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1709 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1710 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1711 wakaba 1.77
1712 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1713 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1714 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1715 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1716 wakaba 1.77
1717 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1718 wakaba 1.77 } else {
1719    
1720 wakaba 1.1 }
1721     } else {
1722     die "$0: $self->{current_token}->{type}: Unknown token type";
1723     }
1724 wakaba 1.59 $self->{state} = DATA_STATE;
1725 wakaba 1.1 # reconsume
1726    
1727     return ($self->{current_token}); # start tag or end tag
1728    
1729     redo A;
1730     } else {
1731 wakaba 1.72 if ({
1732     0x0022 => 1, # "
1733     0x0027 => 1, # '
1734     0x003D => 1, # =
1735 wakaba 1.76 }->{$self->{next_char}}) {
1736 wakaba 1.77
1737 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
1738 wakaba 1.77 } else {
1739    
1740 wakaba 1.72 }
1741 wakaba 1.117 $self->{current_attribute}
1742     = {name => chr ($self->{next_char}),
1743     value => '',
1744     line => $self->{line}, column => $self->{column}};
1745 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
1746 wakaba 1.1
1747 wakaba 1.175 pop @{$self->{prev_char}};
1748     unshift @{$self->{prev_char}}, $self->{next_char};
1749    
1750     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1751     $self->{line_prev} = $self->{line};
1752     $self->{column_prev} = $self->{column};
1753     $self->{column}++;
1754     $self->{next_char}
1755     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1756     } else {
1757     $self->{set_next_char}->($self);
1758     }
1759 wakaba 1.1
1760     redo A;
1761     }
1762 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1763 wakaba 1.1 my $before_leave = sub {
1764     if (exists $self->{current_token}->{attributes} # start tag or end tag
1765     ->{$self->{current_attribute}->{name}}) { # MUST
1766 wakaba 1.77
1767 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'duplicate attribute', text => $self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});
1768 wakaba 1.1 ## Discard $self->{current_attribute} # MUST
1769     } else {
1770 wakaba 1.77
1771 wakaba 1.1 $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}
1772     = $self->{current_attribute};
1773     }
1774     }; # $before_leave
1775    
1776 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1777     $self->{next_char} == 0x000A or # LF
1778     $self->{next_char} == 0x000B or # VT
1779     $self->{next_char} == 0x000C or # FF
1780     $self->{next_char} == 0x0020) { # SP
1781 wakaba 1.77
1782 wakaba 1.1 $before_leave->();
1783 wakaba 1.59 $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1784 wakaba 1.1
1785 wakaba 1.175 pop @{$self->{prev_char}};
1786     unshift @{$self->{prev_char}}, $self->{next_char};
1787    
1788     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1789     $self->{line_prev} = $self->{line};
1790     $self->{column_prev} = $self->{column};
1791     $self->{column}++;
1792     $self->{next_char}
1793     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1794     } else {
1795     $self->{set_next_char}->($self);
1796     }
1797 wakaba 1.1
1798     redo A;
1799 wakaba 1.76 } elsif ($self->{next_char} == 0x003D) { # =
1800 wakaba 1.77
1801 wakaba 1.1 $before_leave->();
1802 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1803 wakaba 1.1
1804 wakaba 1.175 pop @{$self->{prev_char}};
1805     unshift @{$self->{prev_char}}, $self->{next_char};
1806    
1807     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1808     $self->{line_prev} = $self->{line};
1809     $self->{column_prev} = $self->{column};
1810     $self->{column}++;
1811     $self->{next_char}
1812     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1813     } else {
1814     $self->{set_next_char}->($self);
1815     }
1816 wakaba 1.1
1817     redo A;
1818 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1819 wakaba 1.1 $before_leave->();
1820 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1821 wakaba 1.77
1822 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1823 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1824 wakaba 1.77
1825 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1826 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1827 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1828 wakaba 1.1 }
1829     } else {
1830     die "$0: $self->{current_token}->{type}: Unknown token type";
1831     }
1832 wakaba 1.59 $self->{state} = DATA_STATE;
1833 wakaba 1.1
1834 wakaba 1.175 pop @{$self->{prev_char}};
1835     unshift @{$self->{prev_char}}, $self->{next_char};
1836    
1837     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1838     $self->{line_prev} = $self->{line};
1839     $self->{column_prev} = $self->{column};
1840     $self->{column}++;
1841     $self->{next_char}
1842     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1843     } else {
1844     $self->{set_next_char}->($self);
1845     }
1846 wakaba 1.1
1847    
1848     return ($self->{current_token}); # start tag or end tag
1849    
1850     redo A;
1851 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
1852     $self->{next_char} <= 0x005A) { # A..Z
1853 wakaba 1.77
1854 wakaba 1.76 $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);
1855 wakaba 1.1 ## Stay in the state
1856    
1857 wakaba 1.175 pop @{$self->{prev_char}};
1858     unshift @{$self->{prev_char}}, $self->{next_char};
1859    
1860     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1861     $self->{line_prev} = $self->{line};
1862     $self->{column_prev} = $self->{column};
1863     $self->{column}++;
1864     $self->{next_char}
1865     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1866     } else {
1867     $self->{set_next_char}->($self);
1868     }
1869 wakaba 1.1
1870     redo A;
1871 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
1872 wakaba 1.122
1873 wakaba 1.1 $before_leave->();
1874 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1875 wakaba 1.1
1876 wakaba 1.175 pop @{$self->{prev_char}};
1877     unshift @{$self->{prev_char}}, $self->{next_char};
1878    
1879     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1880     $self->{line_prev} = $self->{line};
1881     $self->{column_prev} = $self->{column};
1882     $self->{column}++;
1883     $self->{next_char}
1884     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1885     } else {
1886     $self->{set_next_char}->($self);
1887     }
1888 wakaba 1.1
1889     redo A;
1890 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1891 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1892 wakaba 1.1 $before_leave->();
1893 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1894 wakaba 1.77
1895 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1896 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1897 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1898 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1899 wakaba 1.77
1900 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1901 wakaba 1.77 } else {
1902 wakaba 1.78 ## NOTE: This state should never be reached.
1903 wakaba 1.77
1904 wakaba 1.1 }
1905     } else {
1906     die "$0: $self->{current_token}->{type}: Unknown token type";
1907     }
1908 wakaba 1.59 $self->{state} = DATA_STATE;
1909 wakaba 1.1 # reconsume
1910    
1911     return ($self->{current_token}); # start tag or end tag
1912    
1913     redo A;
1914     } else {
1915 wakaba 1.76 if ($self->{next_char} == 0x0022 or # "
1916     $self->{next_char} == 0x0027) { # '
1917 wakaba 1.77
1918 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
1919 wakaba 1.77 } else {
1920    
1921 wakaba 1.72 }
1922 wakaba 1.76 $self->{current_attribute}->{name} .= chr ($self->{next_char});
1923 wakaba 1.1 ## Stay in the state
1924    
1925 wakaba 1.175 pop @{$self->{prev_char}};
1926     unshift @{$self->{prev_char}}, $self->{next_char};
1927    
1928     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1929     $self->{line_prev} = $self->{line};
1930     $self->{column_prev} = $self->{column};
1931     $self->{column}++;
1932     $self->{next_char}
1933     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1934     } else {
1935     $self->{set_next_char}->($self);
1936     }
1937 wakaba 1.1
1938     redo A;
1939     }
1940 wakaba 1.59 } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1941 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1942     $self->{next_char} == 0x000A or # LF
1943     $self->{next_char} == 0x000B or # VT
1944     $self->{next_char} == 0x000C or # FF
1945     $self->{next_char} == 0x0020) { # SP
1946 wakaba 1.77
1947 wakaba 1.1 ## Stay in the state
1948    
1949 wakaba 1.175 pop @{$self->{prev_char}};
1950     unshift @{$self->{prev_char}}, $self->{next_char};
1951    
1952     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1953     $self->{line_prev} = $self->{line};
1954     $self->{column_prev} = $self->{column};
1955     $self->{column}++;
1956     $self->{next_char}
1957     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1958     } else {
1959     $self->{set_next_char}->($self);
1960     }
1961 wakaba 1.1
1962     redo A;
1963 wakaba 1.76 } elsif ($self->{next_char} == 0x003D) { # =
1964 wakaba 1.77
1965 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1966 wakaba 1.1
1967 wakaba 1.175 pop @{$self->{prev_char}};
1968     unshift @{$self->{prev_char}}, $self->{next_char};
1969    
1970     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1971     $self->{line_prev} = $self->{line};
1972     $self->{column_prev} = $self->{column};
1973     $self->{column}++;
1974     $self->{next_char}
1975     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1976     } else {
1977     $self->{set_next_char}->($self);
1978     }
1979 wakaba 1.1
1980     redo A;
1981 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1982 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1983 wakaba 1.77
1984 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1985 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1986 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1987 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1988 wakaba 1.77
1989 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1990 wakaba 1.77 } else {
1991 wakaba 1.78 ## NOTE: This state should never be reached.
1992 wakaba 1.77
1993 wakaba 1.1 }
1994     } else {
1995     die "$0: $self->{current_token}->{type}: Unknown token type";
1996     }
1997 wakaba 1.59 $self->{state} = DATA_STATE;
1998 wakaba 1.1
1999 wakaba 1.175 pop @{$self->{prev_char}};
2000     unshift @{$self->{prev_char}}, $self->{next_char};
2001    
2002     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2003     $self->{line_prev} = $self->{line};
2004     $self->{column_prev} = $self->{column};
2005     $self->{column}++;
2006     $self->{next_char}
2007     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2008     } else {
2009     $self->{set_next_char}->($self);
2010     }
2011 wakaba 1.1
2012    
2013     return ($self->{current_token}); # start tag or end tag
2014    
2015     redo A;
2016 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
2017     $self->{next_char} <= 0x005A) { # A..Z
2018 wakaba 1.77
2019 wakaba 1.117 $self->{current_attribute}
2020     = {name => chr ($self->{next_char} + 0x0020),
2021     value => '',
2022     line => $self->{line}, column => $self->{column}};
2023 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
2024 wakaba 1.1
2025 wakaba 1.175 pop @{$self->{prev_char}};
2026     unshift @{$self->{prev_char}}, $self->{next_char};
2027    
2028     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2029     $self->{line_prev} = $self->{line};
2030     $self->{column_prev} = $self->{column};
2031     $self->{column}++;
2032     $self->{next_char}
2033     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2034     } else {
2035     $self->{set_next_char}->($self);
2036     }
2037 wakaba 1.1
2038     redo A;
2039 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
2040 wakaba 1.1
2041 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
2042    
2043 wakaba 1.175 pop @{$self->{prev_char}};
2044     unshift @{$self->{prev_char}}, $self->{next_char};
2045    
2046     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2047     $self->{line_prev} = $self->{line};
2048     $self->{column_prev} = $self->{column};
2049     $self->{column}++;
2050     $self->{next_char}
2051     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2052     } else {
2053     $self->{set_next_char}->($self);
2054     }
2055 wakaba 1.1
2056     redo A;
2057 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2058 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2059 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2060 wakaba 1.77
2061 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2062 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2063 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2064 wakaba 1.1 if ($self->{current_token}->{attributes}) {
2065 wakaba 1.77
2066 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2067 wakaba 1.77 } else {
2068 wakaba 1.78 ## NOTE: This state should never be reached.
2069 wakaba 1.77
2070 wakaba 1.1 }
2071     } else {
2072     die "$0: $self->{current_token}->{type}: Unknown token type";
2073     }
2074 wakaba 1.59 $self->{state} = DATA_STATE;
2075 wakaba 1.1 # reconsume
2076    
2077     return ($self->{current_token}); # start tag or end tag
2078    
2079     redo A;
2080     } else {
2081 wakaba 1.153 if ($self->{next_char} == 0x0022 or # "
2082     $self->{next_char} == 0x0027) { # '
2083    
2084     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
2085     } else {
2086    
2087     }
2088 wakaba 1.117 $self->{current_attribute}
2089     = {name => chr ($self->{next_char}),
2090     value => '',
2091     line => $self->{line}, column => $self->{column}};
2092 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
2093 wakaba 1.1
2094 wakaba 1.175 pop @{$self->{prev_char}};
2095     unshift @{$self->{prev_char}}, $self->{next_char};
2096    
2097     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2098     $self->{line_prev} = $self->{line};
2099     $self->{column_prev} = $self->{column};
2100     $self->{column}++;
2101     $self->{next_char}
2102     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2103     } else {
2104     $self->{set_next_char}->($self);
2105     }
2106 wakaba 1.1
2107     redo A;
2108     }
2109 wakaba 1.59 } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
2110 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
2111     $self->{next_char} == 0x000A or # LF
2112     $self->{next_char} == 0x000B or # VT
2113     $self->{next_char} == 0x000C or # FF
2114     $self->{next_char} == 0x0020) { # SP
2115 wakaba 1.77
2116 wakaba 1.1 ## Stay in the state
2117    
2118 wakaba 1.175 pop @{$self->{prev_char}};
2119     unshift @{$self->{prev_char}}, $self->{next_char};
2120    
2121     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2122     $self->{line_prev} = $self->{line};
2123     $self->{column_prev} = $self->{column};
2124     $self->{column}++;
2125     $self->{next_char}
2126     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2127     } else {
2128     $self->{set_next_char}->($self);
2129     }
2130 wakaba 1.1
2131     redo A;
2132 wakaba 1.76 } elsif ($self->{next_char} == 0x0022) { # "
2133 wakaba 1.77
2134 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
2135 wakaba 1.1
2136 wakaba 1.175 pop @{$self->{prev_char}};
2137     unshift @{$self->{prev_char}}, $self->{next_char};
2138    
2139     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2140     $self->{line_prev} = $self->{line};
2141     $self->{column_prev} = $self->{column};
2142     $self->{column}++;
2143     $self->{next_char}
2144     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2145     } else {
2146     $self->{set_next_char}->($self);
2147     }
2148 wakaba 1.1
2149     redo A;
2150 wakaba 1.76 } elsif ($self->{next_char} == 0x0026) { # &
2151 wakaba 1.77
2152 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
2153 wakaba 1.1 ## reconsume
2154     redo A;
2155 wakaba 1.76 } elsif ($self->{next_char} == 0x0027) { # '
2156 wakaba 1.77
2157 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
2158 wakaba 1.1
2159 wakaba 1.175 pop @{$self->{prev_char}};
2160     unshift @{$self->{prev_char}}, $self->{next_char};
2161    
2162     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2163     $self->{line_prev} = $self->{line};
2164     $self->{column_prev} = $self->{column};
2165     $self->{column}++;
2166     $self->{next_char}
2167     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2168     } else {
2169     $self->{set_next_char}->($self);
2170     }
2171 wakaba 1.1
2172     redo A;
2173 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2174 wakaba 1.153 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty unquoted attribute value');
2175 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2176 wakaba 1.77
2177 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2178 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2179 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2180 wakaba 1.1 if ($self->{current_token}->{attributes}) {
2181 wakaba 1.77
2182 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2183 wakaba 1.77 } else {
2184 wakaba 1.78 ## NOTE: This state should never be reached.
2185 wakaba 1.77
2186 wakaba 1.1 }
2187     } else {
2188     die "$0: $self->{current_token}->{type}: Unknown token type";
2189     }
2190 wakaba 1.59 $self->{state} = DATA_STATE;
2191 wakaba 1.1
2192 wakaba 1.175 pop @{$self->{prev_char}};
2193     unshift @{$self->{prev_char}}, $self->{next_char};
2194    
2195     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2196     $self->{line_prev} = $self->{line};
2197     $self->{column_prev} = $self->{column};
2198     $self->{column}++;
2199     $self->{next_char}
2200     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2201     } else {
2202     $self->{set_next_char}->($self);
2203     }
2204 wakaba 1.1
2205    
2206     return ($self->{current_token}); # start tag or end tag
2207    
2208     redo A;
2209 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2210 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2211 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2212 wakaba 1.77
2213 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2214 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2215 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2216 wakaba 1.1 if ($self->{current_token}->{attributes}) {
2217 wakaba 1.77
2218 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2219 wakaba 1.77 } else {
2220 wakaba 1.78 ## NOTE: This state should never be reached.
2221 wakaba 1.77
2222 wakaba 1.1 }
2223     } else {
2224     die "$0: $self->{current_token}->{type}: Unknown token type";
2225     }
2226 wakaba 1.59 $self->{state} = DATA_STATE;
2227 wakaba 1.1 ## reconsume
2228    
2229     return ($self->{current_token}); # start tag or end tag
2230    
2231     redo A;
2232     } else {
2233 wakaba 1.76 if ($self->{next_char} == 0x003D) { # =
2234 wakaba 1.77
2235 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute value');
2236 wakaba 1.77 } else {
2237    
2238 wakaba 1.72 }
2239 wakaba 1.76 $self->{current_attribute}->{value} .= chr ($self->{next_char});
2240 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
2241 wakaba 1.1
2242 wakaba 1.175 pop @{$self->{prev_char}};
2243     unshift @{$self->{prev_char}}, $self->{next_char};
2244    
2245     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2246     $self->{line_prev} = $self->{line};
2247     $self->{column_prev} = $self->{column};
2248     $self->{column}++;
2249     $self->{next_char}
2250     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2251     } else {
2252     $self->{set_next_char}->($self);
2253     }
2254 wakaba 1.1
2255     redo A;
2256     }
2257 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
2258 wakaba 1.76 if ($self->{next_char} == 0x0022) { # "
2259 wakaba 1.77
2260 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
2261 wakaba 1.1
2262 wakaba 1.175 pop @{$self->{prev_char}};
2263     unshift @{$self->{prev_char}}, $self->{next_char};
2264    
2265     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2266     $self->{line_prev} = $self->{line};
2267     $self->{column_prev} = $self->{column};
2268     $self->{column}++;
2269     $self->{next_char}
2270     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2271     } else {
2272     $self->{set_next_char}->($self);
2273     }
2274 wakaba 1.1
2275     redo A;
2276 wakaba 1.76 } elsif ($self->{next_char} == 0x0026) { # &
2277 wakaba 1.77
2278 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2279     ## "entity in attribute value state". In this implementation, the
2280     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2281     ## implementation of the "consume a character reference" algorithm.
2282 wakaba 1.165 $self->{prev_state} = $self->{state};
2283 wakaba 1.163 $self->{entity_additional} = 0x0022; # "
2284     $self->{state} = ENTITY_STATE;
2285 wakaba 1.1
2286 wakaba 1.175 pop @{$self->{prev_char}};
2287     unshift @{$self->{prev_char}}, $self->{next_char};
2288    
2289     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2290     $self->{line_prev} = $self->{line};
2291     $self->{column_prev} = $self->{column};
2292     $self->{column}++;
2293     $self->{next_char}
2294     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2295     } else {
2296     $self->{set_next_char}->($self);
2297     }
2298 wakaba 1.1
2299     redo A;
2300 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2301 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed attribute value');
2302 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2303 wakaba 1.77
2304 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2305 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2306 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2307 wakaba 1.1 if ($self->{current_token}->{attributes}) {
2308 wakaba 1.77
2309 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2310 wakaba 1.77 } else {
2311 wakaba 1.78 ## NOTE: This state should never be reached.
2312 wakaba 1.77
2313 wakaba 1.1 }
2314     } else {
2315     die "$0: $self->{current_token}->{type}: Unknown token type";
2316     }
2317 wakaba 1.59 $self->{state} = DATA_STATE;
2318 wakaba 1.1 ## reconsume
2319    
2320     return ($self->{current_token}); # start tag or end tag
2321    
2322     redo A;
2323     } else {
2324 wakaba 1.77
2325 wakaba 1.76 $self->{current_attribute}->{value} .= chr ($self->{next_char});
2326 wakaba 1.169 $self->{read_until}->($self->{current_attribute}->{value},
2327     q["&],
2328     length $self->{current_attribute}->{value});
2329    
2330 wakaba 1.1 ## Stay in the state
2331    
2332 wakaba 1.175 pop @{$self->{prev_char}};
2333     unshift @{$self->{prev_char}}, $self->{next_char};
2334    
2335     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2336     $self->{line_prev} = $self->{line};
2337     $self->{column_prev} = $self->{column};
2338     $self->{column}++;
2339     $self->{next_char}
2340     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2341     } else {
2342     $self->{set_next_char}->($self);
2343     }
2344 wakaba 1.1
2345     redo A;
2346     }
2347 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
2348 wakaba 1.76 if ($self->{next_char} == 0x0027) { # '
2349 wakaba 1.77
2350 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
2351 wakaba 1.1
2352 wakaba 1.175 pop @{$self->{prev_char}};
2353     unshift @{$self->{prev_char}}, $self->{next_char};
2354    
2355     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2356     $self->{line_prev} = $self->{line};
2357     $self->{column_prev} = $self->{column};
2358     $self->{column}++;
2359     $self->{next_char}
2360     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2361     } else {
2362     $self->{set_next_char}->($self);
2363     }
2364 wakaba 1.1
2365     redo A;
2366 wakaba 1.76 } elsif ($self->{next_char} == 0x0026) { # &
2367 wakaba 1.77
2368 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2369     ## "entity in attribute value state". In this implementation, the
2370     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2371     ## implementation of the "consume a character reference" algorithm.
2372     $self->{entity_additional} = 0x0027; # '
2373 wakaba 1.165 $self->{prev_state} = $self->{state};
2374 wakaba 1.163 $self->{state} = ENTITY_STATE;
2375 wakaba 1.1
2376 wakaba 1.175 pop @{$self->{prev_char}};
2377     unshift @{$self->{prev_char}}, $self->{next_char};
2378    
2379     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2380     $self->{line_prev} = $self->{line};
2381     $self->{column_prev} = $self->{column};
2382     $self->{column}++;
2383     $self->{next_char}
2384     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2385     } else {
2386     $self->{set_next_char}->($self);
2387     }
2388 wakaba 1.1
2389     redo A;
2390 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2391 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed attribute value');
2392 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2393 wakaba 1.77
2394 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2395 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2396 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2397 wakaba 1.1 if ($self->{current_token}->{attributes}) {
2398 wakaba 1.77
2399 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2400 wakaba 1.77 } else {
2401 wakaba 1.78 ## NOTE: This state should never be reached.
2402 wakaba 1.77
2403 wakaba 1.1 }
2404     } else {
2405     die "$0: $self->{current_token}->{type}: Unknown token type";
2406     }
2407 wakaba 1.59 $self->{state} = DATA_STATE;
2408 wakaba 1.1 ## reconsume
2409    
2410     return ($self->{current_token}); # start tag or end tag
2411    
2412     redo A;
2413     } else {
2414 wakaba 1.77
2415 wakaba 1.76 $self->{current_attribute}->{value} .= chr ($self->{next_char});
2416 wakaba 1.169 $self->{read_until}->($self->{current_attribute}->{value},
2417     q['&],
2418     length $self->{current_attribute}->{value});
2419    
2420 wakaba 1.1 ## Stay in the state
2421    
2422 wakaba 1.175 pop @{$self->{prev_char}};
2423     unshift @{$self->{prev_char}}, $self->{next_char};
2424    
2425     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2426     $self->{line_prev} = $self->{line};
2427     $self->{column_prev} = $self->{column};
2428     $self->{column}++;
2429     $self->{next_char}
2430     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2431     } else {
2432     $self->{set_next_char}->($self);
2433     }
2434 wakaba 1.1
2435     redo A;
2436     }
2437 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
2438 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
2439     $self->{next_char} == 0x000A or # LF
2440     $self->{next_char} == 0x000B or # HT
2441     $self->{next_char} == 0x000C or # FF
2442     $self->{next_char} == 0x0020) { # SP
2443 wakaba 1.77
2444 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2445 wakaba 1.1
2446 wakaba 1.175 pop @{$self->{prev_char}};
2447     unshift @{$self->{prev_char}}, $self->{next_char};
2448    
2449     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2450     $self->{line_prev} = $self->{line};
2451     $self->{column_prev} = $self->{column};
2452     $self->{column}++;
2453     $self->{next_char}
2454     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2455     } else {
2456     $self->{set_next_char}->($self);
2457     }
2458 wakaba 1.1
2459     redo A;
2460 wakaba 1.76 } elsif ($self->{next_char} == 0x0026) { # &
2461 wakaba 1.77
2462 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2463     ## "entity in attribute value state". In this implementation, the
2464     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2465     ## implementation of the "consume a character reference" algorithm.
2466     $self->{entity_additional} = -1;
2467 wakaba 1.165 $self->{prev_state} = $self->{state};
2468 wakaba 1.163 $self->{state} = ENTITY_STATE;
2469 wakaba 1.1
2470 wakaba 1.175 pop @{$self->{prev_char}};
2471     unshift @{$self->{prev_char}}, $self->{next_char};
2472    
2473     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2474     $self->{line_prev} = $self->{line};
2475     $self->{column_prev} = $self->{column};
2476     $self->{column}++;
2477     $self->{next_char}
2478     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2479     } else {
2480     $self->{set_next_char}->($self);
2481     }
2482 wakaba 1.1
2483     redo A;
2484 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2485 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2486 wakaba 1.77
2487 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2488 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2489 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2490 wakaba 1.1 if ($self->{current_token}->{attributes}) {
2491 wakaba 1.77
2492 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2493 wakaba 1.77 } else {
2494 wakaba 1.78 ## NOTE: This state should never be reached.
2495 wakaba 1.77
2496 wakaba 1.1 }
2497     } else {
2498     die "$0: $self->{current_token}->{type}: Unknown token type";
2499     }
2500 wakaba 1.59 $self->{state} = DATA_STATE;
2501 wakaba 1.1
2502 wakaba 1.175 pop @{$self->{prev_char}};
2503     unshift @{$self->{prev_char}}, $self->{next_char};
2504    
2505     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2506     $self->{line_prev} = $self->{line};
2507     $self->{column_prev} = $self->{column};
2508     $self->{column}++;
2509     $self->{next_char}
2510     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2511     } else {
2512     $self->{set_next_char}->($self);
2513     }
2514 wakaba 1.1
2515    
2516     return ($self->{current_token}); # start tag or end tag
2517    
2518     redo A;
2519 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2520 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2521 wakaba 1.57 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2522 wakaba 1.77
2523 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2524 wakaba 1.57 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2525 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2526 wakaba 1.1 if ($self->{current_token}->{attributes}) {
2527 wakaba 1.77
2528 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2529 wakaba 1.77 } else {
2530 wakaba 1.78 ## NOTE: This state should never be reached.
2531 wakaba 1.77
2532 wakaba 1.1 }
2533     } else {
2534     die "$0: $self->{current_token}->{type}: Unknown token type";
2535     }
2536 wakaba 1.59 $self->{state} = DATA_STATE;
2537 wakaba 1.1 ## reconsume
2538    
2539     return ($self->{current_token}); # start tag or end tag
2540    
2541     redo A;
2542     } else {
2543 wakaba 1.72 if ({
2544     0x0022 => 1, # "
2545     0x0027 => 1, # '
2546     0x003D => 1, # =
2547 wakaba 1.76 }->{$self->{next_char}}) {
2548 wakaba 1.77
2549 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute value');
2550 wakaba 1.77 } else {
2551    
2552 wakaba 1.72 }
2553 wakaba 1.76 $self->{current_attribute}->{value} .= chr ($self->{next_char});
2554 wakaba 1.169 $self->{read_until}->($self->{current_attribute}->{value},
2555     q["'=& >],
2556     length $self->{current_attribute}->{value});
2557    
2558 wakaba 1.1 ## Stay in the state
2559    
2560 wakaba 1.175 pop @{$self->{prev_char}};
2561     unshift @{$self->{prev_char}}, $self->{next_char};
2562    
2563     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2564     $self->{line_prev} = $self->{line};
2565     $self->{column_prev} = $self->{column};
2566     $self->{column}++;
2567     $self->{next_char}
2568     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2569     } else {
2570     $self->{set_next_char}->($self);
2571     }
2572 wakaba 1.1
2573     redo A;
2574     }
2575 wakaba 1.72 } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2576 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
2577     $self->{next_char} == 0x000A or # LF
2578     $self->{next_char} == 0x000B or # VT
2579     $self->{next_char} == 0x000C or # FF
2580     $self->{next_char} == 0x0020) { # SP
2581 wakaba 1.77
2582 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2583    
2584 wakaba 1.175 pop @{$self->{prev_char}};
2585     unshift @{$self->{prev_char}}, $self->{next_char};
2586    
2587     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2588     $self->{line_prev} = $self->{line};
2589     $self->{column_prev} = $self->{column};
2590     $self->{column}++;
2591     $self->{next_char}
2592     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2593     } else {
2594     $self->{set_next_char}->($self);
2595     }
2596 wakaba 1.72
2597     redo A;
2598 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2599 wakaba 1.72 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2600 wakaba 1.77
2601 wakaba 1.72 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2602     } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2603     $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2604     if ($self->{current_token}->{attributes}) {
2605 wakaba 1.77
2606 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2607 wakaba 1.77 } else {
2608 wakaba 1.78 ## NOTE: This state should never be reached.
2609 wakaba 1.77
2610 wakaba 1.72 }
2611     } else {
2612     die "$0: $self->{current_token}->{type}: Unknown token type";
2613     }
2614     $self->{state} = DATA_STATE;
2615    
2616 wakaba 1.175 pop @{$self->{prev_char}};
2617     unshift @{$self->{prev_char}}, $self->{next_char};
2618    
2619     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2620     $self->{line_prev} = $self->{line};
2621     $self->{column_prev} = $self->{column};
2622     $self->{column}++;
2623     $self->{next_char}
2624     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2625     } else {
2626     $self->{set_next_char}->($self);
2627     }
2628 wakaba 1.72
2629    
2630     return ($self->{current_token}); # start tag or end tag
2631    
2632     redo A;
2633 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
2634 wakaba 1.72
2635 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
2636    
2637 wakaba 1.175 pop @{$self->{prev_char}};
2638     unshift @{$self->{prev_char}}, $self->{next_char};
2639    
2640     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2641     $self->{line_prev} = $self->{line};
2642     $self->{column_prev} = $self->{column};
2643     $self->{column}++;
2644     $self->{next_char}
2645     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2646     } else {
2647     $self->{set_next_char}->($self);
2648     }
2649 wakaba 1.72
2650 wakaba 1.122 redo A;
2651 wakaba 1.138 } elsif ($self->{next_char} == -1) {
2652 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2653 wakaba 1.138 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2654    
2655     $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2656     } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2657     if ($self->{current_token}->{attributes}) {
2658    
2659 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2660 wakaba 1.138 } else {
2661     ## NOTE: This state should never be reached.
2662    
2663     }
2664     } else {
2665     die "$0: $self->{current_token}->{type}: Unknown token type";
2666     }
2667     $self->{state} = DATA_STATE;
2668     ## Reconsume.
2669     return ($self->{current_token}); # start tag or end tag
2670     redo A;
2671 wakaba 1.122 } else {
2672    
2673 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no space between attributes');
2674 wakaba 1.122 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2675     ## reconsume
2676     redo A;
2677     }
2678     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2679     if ($self->{next_char} == 0x003E) { # >
2680     if ($self->{current_token}->{type} == END_TAG_TOKEN) {
2681 wakaba 1.77
2682 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc', token => $self->{current_token});
2683 wakaba 1.122 ## TODO: Different type than slash in start tag
2684     $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2685     if ($self->{current_token}->{attributes}) {
2686    
2687 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2688 wakaba 1.122 } else {
2689    
2690     }
2691     ## TODO: Test |<title></title/>|
2692 wakaba 1.72 } else {
2693 wakaba 1.77
2694 wakaba 1.122 $self->{self_closing} = 1;
2695 wakaba 1.72 }
2696 wakaba 1.122
2697     $self->{state} = DATA_STATE;
2698    
2699 wakaba 1.175 pop @{$self->{prev_char}};
2700     unshift @{$self->{prev_char}}, $self->{next_char};
2701    
2702     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2703     $self->{line_prev} = $self->{line};
2704     $self->{column_prev} = $self->{column};
2705     $self->{column}++;
2706     $self->{next_char}
2707     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2708     } else {
2709     $self->{set_next_char}->($self);
2710     }
2711 wakaba 1.122
2712    
2713     return ($self->{current_token}); # start tag or end tag
2714    
2715 wakaba 1.72 redo A;
2716 wakaba 1.138 } elsif ($self->{next_char} == -1) {
2717 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2718 wakaba 1.138 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
2719    
2720     $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
2721     } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
2722     if ($self->{current_token}->{attributes}) {
2723    
2724 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2725 wakaba 1.138 } else {
2726     ## NOTE: This state should never be reached.
2727    
2728     }
2729     } else {
2730     die "$0: $self->{current_token}->{type}: Unknown token type";
2731     }
2732     $self->{state} = DATA_STATE;
2733     ## Reconsume.
2734     return ($self->{current_token}); # start tag or end tag
2735     redo A;
2736 wakaba 1.72 } else {
2737 wakaba 1.77
2738 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc');
2739 wakaba 1.122 ## TODO: This error type is wrong.
2740 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2741 wakaba 1.122 ## Reconsume.
2742 wakaba 1.72 redo A;
2743     }
2744 wakaba 1.59 } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2745 wakaba 1.1 ## (only happen if PCDATA state)
2746 wakaba 1.163
2747     ## NOTE: Unlike spec's "bogus comment state", this implementation
2748     ## consumes characters one-by-one basis.
2749 wakaba 1.1
2750 wakaba 1.163 if ($self->{next_char} == 0x003E) { # >
2751    
2752     $self->{state} = DATA_STATE;
2753    
2754 wakaba 1.175 pop @{$self->{prev_char}};
2755     unshift @{$self->{prev_char}}, $self->{next_char};
2756    
2757     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2758     $self->{line_prev} = $self->{line};
2759     $self->{column_prev} = $self->{column};
2760     $self->{column}++;
2761     $self->{next_char}
2762     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2763     } else {
2764     $self->{set_next_char}->($self);
2765     }
2766 wakaba 1.1
2767    
2768 wakaba 1.163 return ($self->{current_token}); # comment
2769     redo A;
2770     } elsif ($self->{next_char} == -1) {
2771    
2772     $self->{state} = DATA_STATE;
2773     ## reconsume
2774 wakaba 1.1
2775 wakaba 1.163 return ($self->{current_token}); # comment
2776     redo A;
2777     } else {
2778    
2779     $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
2780 wakaba 1.169 $self->{read_until}->($self->{current_token}->{data},
2781     q[>],
2782     length $self->{current_token}->{data});
2783    
2784 wakaba 1.163 ## Stay in the state.
2785    
2786 wakaba 1.175 pop @{$self->{prev_char}};
2787     unshift @{$self->{prev_char}}, $self->{next_char};
2788    
2789     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2790     $self->{line_prev} = $self->{line};
2791     $self->{column_prev} = $self->{column};
2792     $self->{column}++;
2793     $self->{next_char}
2794     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2795     } else {
2796     $self->{set_next_char}->($self);
2797     }
2798 wakaba 1.1
2799 wakaba 1.163 redo A;
2800     }
2801 wakaba 1.59 } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2802 wakaba 1.1 ## (only happen if PCDATA state)
2803    
2804 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
2805 wakaba 1.1
2806 wakaba 1.159 $self->{state} = MD_HYPHEN_STATE;
2807    
2808 wakaba 1.175 pop @{$self->{prev_char}};
2809     unshift @{$self->{prev_char}}, $self->{next_char};
2810    
2811     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2812     $self->{line_prev} = $self->{line};
2813     $self->{column_prev} = $self->{column};
2814     $self->{column}++;
2815     $self->{next_char}
2816     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2817     } else {
2818     $self->{set_next_char}->($self);
2819     }
2820 wakaba 1.1
2821 wakaba 1.159 redo A;
2822 wakaba 1.76 } elsif ($self->{next_char} == 0x0044 or # D
2823     $self->{next_char} == 0x0064) { # d
2824 wakaba 1.159 ## ASCII case-insensitive.
2825    
2826     $self->{state} = MD_DOCTYPE_STATE;
2827     $self->{state_keyword} = chr $self->{next_char};
2828 wakaba 1.1
2829 wakaba 1.175 pop @{$self->{prev_char}};
2830     unshift @{$self->{prev_char}}, $self->{next_char};
2831    
2832     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2833     $self->{line_prev} = $self->{line};
2834     $self->{column_prev} = $self->{column};
2835     $self->{column}++;
2836     $self->{next_char}
2837     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2838     } else {
2839     $self->{set_next_char}->($self);
2840     }
2841 wakaba 1.1
2842 wakaba 1.159 redo A;
2843     } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2844     $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2845     $self->{next_char} == 0x005B) { # [
2846    
2847     $self->{state} = MD_CDATA_STATE;
2848     $self->{state_keyword} = '[';
2849    
2850 wakaba 1.175 pop @{$self->{prev_char}};
2851     unshift @{$self->{prev_char}}, $self->{next_char};
2852    
2853     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2854     $self->{line_prev} = $self->{line};
2855     $self->{column_prev} = $self->{column};
2856     $self->{column}++;
2857     $self->{next_char}
2858     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2859     } else {
2860     $self->{set_next_char}->($self);
2861     }
2862 wakaba 1.1
2863 wakaba 1.159 redo A;
2864 wakaba 1.1 } else {
2865 wakaba 1.159
2866 wakaba 1.1 }
2867 wakaba 1.159
2868     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2869     line => $self->{line_prev},
2870 wakaba 1.160 column => $self->{column_prev} - 1);
2871 wakaba 1.159 ## Reconsume.
2872     $self->{state} = BOGUS_COMMENT_STATE;
2873     $self->{current_token} = {type => COMMENT_TOKEN, data => '',
2874     line => $self->{line_prev},
2875     column => $self->{column_prev} - 1,
2876     };
2877     redo A;
2878     } elsif ($self->{state} == MD_HYPHEN_STATE) {
2879     if ($self->{next_char} == 0x002D) { # -
2880    
2881     $self->{current_token} = {type => COMMENT_TOKEN, data => '',
2882     line => $self->{line_prev},
2883     column => $self->{column_prev} - 2,
2884     };
2885     $self->{state} = COMMENT_START_STATE;
2886    
2887 wakaba 1.175 pop @{$self->{prev_char}};
2888     unshift @{$self->{prev_char}}, $self->{next_char};
2889    
2890     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2891     $self->{line_prev} = $self->{line};
2892     $self->{column_prev} = $self->{column};
2893     $self->{column}++;
2894     $self->{next_char}
2895     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2896     } else {
2897     $self->{set_next_char}->($self);
2898     }
2899 wakaba 1.1
2900 wakaba 1.159 redo A;
2901 wakaba 1.1 } else {
2902 wakaba 1.159
2903     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2904     line => $self->{line_prev},
2905     column => $self->{column_prev} - 2);
2906     $self->{state} = BOGUS_COMMENT_STATE;
2907     ## Reconsume.
2908     $self->{current_token} = {type => COMMENT_TOKEN,
2909     data => '-',
2910     line => $self->{line_prev},
2911     column => $self->{column_prev} - 2,
2912     };
2913     redo A;
2914 wakaba 1.1 }
2915 wakaba 1.159 } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2916     ## ASCII case-insensitive.
2917     if ($self->{next_char} == [
2918     undef,
2919     0x004F, # O
2920     0x0043, # C
2921     0x0054, # T
2922     0x0059, # Y
2923     0x0050, # P
2924     ]->[length $self->{state_keyword}] or
2925     $self->{next_char} == [
2926     undef,
2927     0x006F, # o
2928     0x0063, # c
2929     0x0074, # t
2930     0x0079, # y
2931     0x0070, # p
2932     ]->[length $self->{state_keyword}]) {
2933    
2934     ## Stay in the state.
2935     $self->{state_keyword} .= chr $self->{next_char};
2936    
2937 wakaba 1.175 pop @{$self->{prev_char}};
2938     unshift @{$self->{prev_char}}, $self->{next_char};
2939    
2940     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2941     $self->{line_prev} = $self->{line};
2942     $self->{column_prev} = $self->{column};
2943     $self->{column}++;
2944     $self->{next_char}
2945     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2946     } else {
2947     $self->{set_next_char}->($self);
2948     }
2949 wakaba 1.1
2950 wakaba 1.159 redo A;
2951     } elsif ((length $self->{state_keyword}) == 6 and
2952     ($self->{next_char} == 0x0045 or # E
2953     $self->{next_char} == 0x0065)) { # e
2954    
2955     $self->{state} = DOCTYPE_STATE;
2956     $self->{current_token} = {type => DOCTYPE_TOKEN,
2957     quirks => 1,
2958     line => $self->{line_prev},
2959     column => $self->{column_prev} - 7,
2960     };
2961 wakaba 1.124
2962 wakaba 1.175 pop @{$self->{prev_char}};
2963     unshift @{$self->{prev_char}}, $self->{next_char};
2964    
2965     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2966     $self->{line_prev} = $self->{line};
2967     $self->{column_prev} = $self->{column};
2968     $self->{column}++;
2969     $self->{next_char}
2970     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2971     } else {
2972     $self->{set_next_char}->($self);
2973     }
2974 wakaba 1.124
2975 wakaba 1.159 redo A;
2976 wakaba 1.124 } else {
2977    
2978 wakaba 1.159 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2979     line => $self->{line_prev},
2980     column => $self->{column_prev} - 1 - length $self->{state_keyword});
2981     $self->{state} = BOGUS_COMMENT_STATE;
2982     ## Reconsume.
2983     $self->{current_token} = {type => COMMENT_TOKEN,
2984     data => $self->{state_keyword},
2985     line => $self->{line_prev},
2986     column => $self->{column_prev} - 1 - length $self->{state_keyword},
2987     };
2988     redo A;
2989 wakaba 1.124 }
2990 wakaba 1.159 } elsif ($self->{state} == MD_CDATA_STATE) {
2991     if ($self->{next_char} == {
2992     '[' => 0x0043, # C
2993     '[C' => 0x0044, # D
2994     '[CD' => 0x0041, # A
2995     '[CDA' => 0x0054, # T
2996     '[CDAT' => 0x0041, # A
2997     }->{$self->{state_keyword}}) {
2998    
2999     ## Stay in the state.
3000     $self->{state_keyword} .= chr $self->{next_char};
3001    
3002 wakaba 1.175 pop @{$self->{prev_char}};
3003     unshift @{$self->{prev_char}}, $self->{next_char};
3004    
3005     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3006     $self->{line_prev} = $self->{line};
3007     $self->{column_prev} = $self->{column};
3008     $self->{column}++;
3009     $self->{next_char}
3010     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3011     } else {
3012     $self->{set_next_char}->($self);
3013     }
3014 wakaba 1.124
3015 wakaba 1.159 redo A;
3016     } elsif ($self->{state_keyword} eq '[CDATA' and
3017     $self->{next_char} == 0x005B) { # [
3018    
3019 wakaba 1.161 $self->{current_token} = {type => CHARACTER_TOKEN,
3020     data => '',
3021     line => $self->{line_prev},
3022     column => $self->{column_prev} - 7};
3023     $self->{state} = CDATA_SECTION_STATE;
3024 wakaba 1.159
3025 wakaba 1.175 pop @{$self->{prev_char}};
3026     unshift @{$self->{prev_char}}, $self->{next_char};
3027    
3028     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3029     $self->{line_prev} = $self->{line};
3030     $self->{column_prev} = $self->{column};
3031     $self->{column}++;
3032     $self->{next_char}
3033     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3034     } else {
3035     $self->{set_next_char}->($self);
3036     }
3037 wakaba 1.124
3038 wakaba 1.159 redo A;
3039 wakaba 1.77 } else {
3040    
3041 wakaba 1.159 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
3042     line => $self->{line_prev},
3043     column => $self->{column_prev} - 1 - length $self->{state_keyword});
3044     $self->{state} = BOGUS_COMMENT_STATE;
3045     ## Reconsume.
3046     $self->{current_token} = {type => COMMENT_TOKEN,
3047     data => $self->{state_keyword},
3048     line => $self->{line_prev},
3049     column => $self->{column_prev} - 1 - length $self->{state_keyword},
3050     };
3051     redo A;
3052 wakaba 1.1 }
3053 wakaba 1.59 } elsif ($self->{state} == COMMENT_START_STATE) {
3054 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
3055 wakaba 1.77
3056 wakaba 1.59 $self->{state} = COMMENT_START_DASH_STATE;
3057 wakaba 1.23
3058 wakaba 1.175 pop @{$self->{prev_char}};
3059     unshift @{$self->{prev_char}}, $self->{next_char};
3060    
3061     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3062     $self->{line_prev} = $self->{line};
3063     $self->{column_prev} = $self->{column};
3064     $self->{column}++;
3065     $self->{next_char}
3066     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3067     } else {
3068     $self->{set_next_char}->($self);
3069     }
3070 wakaba 1.23
3071     redo A;
3072 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3073 wakaba 1.77
3074 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment');
3075 wakaba 1.59 $self->{state} = DATA_STATE;
3076 wakaba 1.23
3077 wakaba 1.175 pop @{$self->{prev_char}};
3078     unshift @{$self->{prev_char}}, $self->{next_char};
3079    
3080     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3081     $self->{line_prev} = $self->{line};
3082     $self->{column_prev} = $self->{column};
3083     $self->{column}++;
3084     $self->{next_char}
3085     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3086     } else {
3087     $self->{set_next_char}->($self);
3088     }
3089 wakaba 1.23
3090    
3091     return ($self->{current_token}); # comment
3092    
3093     redo A;
3094 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3095 wakaba 1.77
3096 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3097 wakaba 1.59 $self->{state} = DATA_STATE;
3098 wakaba 1.23 ## reconsume
3099    
3100     return ($self->{current_token}); # comment
3101    
3102 wakaba 1.77 redo A;
3103     } else {
3104    
3105 wakaba 1.23 $self->{current_token}->{data} # comment
3106 wakaba 1.76 .= chr ($self->{next_char});
3107 wakaba 1.59 $self->{state} = COMMENT_STATE;
3108 wakaba 1.23
3109 wakaba 1.175 pop @{$self->{prev_char}};
3110     unshift @{$self->{prev_char}}, $self->{next_char};
3111    
3112     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3113     $self->{line_prev} = $self->{line};
3114     $self->{column_prev} = $self->{column};
3115     $self->{column}++;
3116     $self->{next_char}
3117     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3118     } else {
3119     $self->{set_next_char}->($self);
3120     }
3121 wakaba 1.23
3122     redo A;
3123     }
3124 wakaba 1.59 } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
3125 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
3126 wakaba 1.77
3127 wakaba 1.59 $self->{state} = COMMENT_END_STATE;
3128 wakaba 1.23
3129 wakaba 1.175 pop @{$self->{prev_char}};
3130     unshift @{$self->{prev_char}}, $self->{next_char};
3131    
3132     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3133     $self->{line_prev} = $self->{line};
3134     $self->{column_prev} = $self->{column};
3135     $self->{column}++;
3136     $self->{next_char}
3137     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3138     } else {
3139     $self->{set_next_char}->($self);
3140     }
3141 wakaba 1.23
3142     redo A;
3143 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3144 wakaba 1.77
3145 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment');
3146 wakaba 1.59 $self->{state} = DATA_STATE;
3147 wakaba 1.23
3148 wakaba 1.175 pop @{$self->{prev_char}};
3149     unshift @{$self->{prev_char}}, $self->{next_char};
3150    
3151     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3152     $self->{line_prev} = $self->{line};
3153     $self->{column_prev} = $self->{column};
3154     $self->{column}++;
3155     $self->{next_char}
3156     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3157     } else {
3158     $self->{set_next_char}->($self);
3159     }
3160 wakaba 1.23
3161    
3162     return ($self->{current_token}); # comment
3163    
3164     redo A;
3165 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3166 wakaba 1.77
3167 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3168 wakaba 1.59 $self->{state} = DATA_STATE;
3169 wakaba 1.23 ## reconsume
3170    
3171     return ($self->{current_token}); # comment
3172    
3173     redo A;
3174     } else {
3175 wakaba 1.77
3176 wakaba 1.23 $self->{current_token}->{data} # comment
3177 wakaba 1.76 .= '-' . chr ($self->{next_char});
3178 wakaba 1.59 $self->{state} = COMMENT_STATE;
3179 wakaba 1.23
3180 wakaba 1.175 pop @{$self->{prev_char}};
3181     unshift @{$self->{prev_char}}, $self->{next_char};
3182    
3183     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3184     $self->{line_prev} = $self->{line};
3185     $self->{column_prev} = $self->{column};
3186     $self->{column}++;
3187     $self->{next_char}
3188     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3189     } else {
3190     $self->{set_next_char}->($self);
3191     }
3192 wakaba 1.23
3193     redo A;
3194     }
3195 wakaba 1.59 } elsif ($self->{state} == COMMENT_STATE) {
3196 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
3197 wakaba 1.77
3198 wakaba 1.59 $self->{state} = COMMENT_END_DASH_STATE;
3199 wakaba 1.1
3200 wakaba 1.175 pop @{$self->{prev_char}};
3201     unshift @{$self->{prev_char}}, $self->{next_char};
3202    
3203     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3204     $self->{line_prev} = $self->{line};
3205     $self->{column_prev} = $self->{column};
3206     $self->{column}++;
3207     $self->{next_char}
3208     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3209     } else {
3210     $self->{set_next_char}->($self);
3211     }
3212 wakaba 1.1
3213     redo A;
3214 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3215 wakaba 1.77
3216 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3217 wakaba 1.59 $self->{state} = DATA_STATE;
3218 wakaba 1.1 ## reconsume
3219    
3220     return ($self->{current_token}); # comment
3221    
3222     redo A;
3223     } else {
3224 wakaba 1.77
3225 wakaba 1.76 $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
3226 wakaba 1.169 $self->{read_until}->($self->{current_token}->{data},
3227     q[-],
3228     length $self->{current_token}->{data});
3229    
3230 wakaba 1.1 ## Stay in the state
3231    
3232 wakaba 1.175 pop @{$self->{prev_char}};
3233     unshift @{$self->{prev_char}}, $self->{next_char};
3234    
3235     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3236     $self->{line_prev} = $self->{line};
3237     $self->{column_prev} = $self->{column};
3238     $self->{column}++;
3239     $self->{next_char}
3240     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3241     } else {
3242     $self->{set_next_char}->($self);
3243     }
3244 wakaba 1.1
3245     redo A;
3246     }
3247 wakaba 1.59 } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
3248 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
3249 wakaba 1.77
3250 wakaba 1.59 $self->{state} = COMMENT_END_STATE;
3251 wakaba 1.1
3252 wakaba 1.175 pop @{$self->{prev_char}};
3253     unshift @{$self->{prev_char}}, $self->{next_char};
3254    
3255     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3256     $self->{line_prev} = $self->{line};
3257     $self->{column_prev} = $self->{column};
3258     $self->{column}++;
3259     $self->{next_char}
3260     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3261     } else {
3262     $self->{set_next_char}->($self);
3263     }
3264 wakaba 1.1
3265     redo A;
3266 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3267 wakaba 1.77
3268 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3269 wakaba 1.59 $self->{state} = DATA_STATE;
3270 wakaba 1.1 ## reconsume
3271    
3272     return ($self->{current_token}); # comment
3273    
3274     redo A;
3275     } else {
3276 wakaba 1.77
3277 wakaba 1.76 $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment
3278 wakaba 1.59 $self->{state} = COMMENT_STATE;
3279 wakaba 1.1
3280 wakaba 1.175 pop @{$self->{prev_char}};
3281     unshift @{$self->{prev_char}}, $self->{next_char};
3282    
3283     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3284     $self->{line_prev} = $self->{line};
3285     $self->{column_prev} = $self->{column};
3286     $self->{column}++;
3287     $self->{next_char}
3288     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3289     } else {
3290     $self->{set_next_char}->($self);
3291     }
3292 wakaba 1.1
3293     redo A;
3294     }
3295 wakaba 1.59 } elsif ($self->{state} == COMMENT_END_STATE) {
3296 wakaba 1.76 if ($self->{next_char} == 0x003E) { # >
3297 wakaba 1.77
3298 wakaba 1.59 $self->{state} = DATA_STATE;
3299 wakaba 1.1
3300 wakaba 1.175 pop @{$self->{prev_char}};
3301     unshift @{$self->{prev_char}}, $self->{next_char};
3302    
3303     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3304     $self->{line_prev} = $self->{line};
3305     $self->{column_prev} = $self->{column};
3306     $self->{column}++;
3307     $self->{next_char}
3308     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3309     } else {
3310     $self->{set_next_char}->($self);
3311     }
3312 wakaba 1.1
3313    
3314     return ($self->{current_token}); # comment
3315    
3316     redo A;
3317 wakaba 1.76 } elsif ($self->{next_char} == 0x002D) { # -
3318 wakaba 1.77
3319 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'dash in comment',
3320 wakaba 1.112 line => $self->{line_prev},
3321     column => $self->{column_prev});
3322 wakaba 1.1 $self->{current_token}->{data} .= '-'; # comment
3323     ## Stay in the state
3324    
3325 wakaba 1.175 pop @{$self->{prev_char}};
3326     unshift @{$self->{prev_char}}, $self->{next_char};
3327    
3328     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3329     $self->{line_prev} = $self->{line};
3330     $self->{column_prev} = $self->{column};
3331     $self->{column}++;
3332     $self->{next_char}
3333     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3334     } else {
3335     $self->{set_next_char}->($self);
3336     }
3337 wakaba 1.1
3338     redo A;
3339 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3340 wakaba 1.77
3341 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3342 wakaba 1.59 $self->{state} = DATA_STATE;
3343 wakaba 1.1 ## reconsume
3344    
3345     return ($self->{current_token}); # comment
3346    
3347     redo A;
3348     } else {
3349 wakaba 1.77
3350 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'dash in comment',
3351 wakaba 1.112 line => $self->{line_prev},
3352     column => $self->{column_prev});
3353 wakaba 1.76 $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment
3354 wakaba 1.59 $self->{state} = COMMENT_STATE;
3355 wakaba 1.1
3356 wakaba 1.175 pop @{$self->{prev_char}};
3357     unshift @{$self->{prev_char}}, $self->{next_char};
3358    
3359     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3360     $self->{line_prev} = $self->{line};
3361     $self->{column_prev} = $self->{column};
3362     $self->{column}++;
3363     $self->{next_char}
3364     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3365     } else {
3366     $self->{set_next_char}->($self);
3367     }
3368 wakaba 1.1
3369     redo A;
3370     }
3371 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_STATE) {
3372 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
3373     $self->{next_char} == 0x000A or # LF
3374     $self->{next_char} == 0x000B or # VT
3375     $self->{next_char} == 0x000C or # FF
3376     $self->{next_char} == 0x0020) { # SP
3377 wakaba 1.77
3378 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
3379 wakaba 1.1
3380 wakaba 1.175 pop @{$self->{prev_char}};
3381     unshift @{$self->{prev_char}}, $self->{next_char};
3382    
3383     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3384     $self->{line_prev} = $self->{line};
3385     $self->{column_prev} = $self->{column};
3386     $self->{column}++;
3387     $self->{next_char}
3388     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3389     } else {
3390     $self->{set_next_char}->($self);
3391     }
3392 wakaba 1.1
3393     redo A;
3394     } else {
3395 wakaba 1.77
3396 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no space before DOCTYPE name');
3397 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
3398 wakaba 1.1 ## reconsume
3399     redo A;
3400     }
3401 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
3402 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
3403     $self->{next_char} == 0x000A or # LF
3404     $self->{next_char} == 0x000B or # VT
3405     $self->{next_char} == 0x000C or # FF
3406     $self->{next_char} == 0x0020) { # SP
3407 wakaba 1.77
3408 wakaba 1.1 ## Stay in the state
3409    
3410 wakaba 1.175 pop @{$self->{prev_char}};
3411     unshift @{$self->{prev_char}}, $self->{next_char};
3412    
3413     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3414     $self->{line_prev} = $self->{line};
3415     $self->{column_prev} = $self->{column};
3416     $self->{column}++;
3417     $self->{next_char}
3418     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3419     } else {
3420     $self->{set_next_char}->($self);
3421     }
3422 wakaba 1.1
3423     redo A;
3424 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3425 wakaba 1.77
3426 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE name');
3427 wakaba 1.59 $self->{state} = DATA_STATE;
3428 wakaba 1.18
3429 wakaba 1.175 pop @{$self->{prev_char}};
3430     unshift @{$self->{prev_char}}, $self->{next_char};
3431    
3432     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3433     $self->{line_prev} = $self->{line};
3434     $self->{column_prev} = $self->{column};
3435     $self->{column}++;
3436     $self->{next_char}
3437     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3438     } else {
3439     $self->{set_next_char}->($self);
3440     }
3441 wakaba 1.18
3442    
3443 wakaba 1.110 return ($self->{current_token}); # DOCTYPE (quirks)
3444 wakaba 1.18
3445     redo A;
3446 wakaba 1.77 } elsif ($self->{next_char} == -1) {
3447    
3448 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE name');
3449 wakaba 1.59 $self->{state} = DATA_STATE;
3450 wakaba 1.18 ## reconsume
3451    
3452 wakaba 1.110 return ($self->{current_token}); # DOCTYPE (quirks)
3453 wakaba 1.18
3454     redo A;
3455     } else {
3456 wakaba 1.77
3457 wakaba 1.110 $self->{current_token}->{name} = chr $self->{next_char};
3458     delete $self->{current_token}->{quirks};
3459 wakaba 1.4 ## ISSUE: "Set the token's name name to the" in the spec
3460 wakaba 1.59 $self->{state} = DOCTYPE_NAME_STATE;
3461 wakaba 1.1
3462 wakaba 1.175 pop @{$self->{prev_char}};
3463     unshift @{$self->{prev_char}}, $self->{next_char};
3464    
3465     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3466     $self->{line_prev} = $self->{line};
3467     $self->{column_prev} = $self->{column};
3468     $self->{column}++;
3469     $self->{next_char}
3470     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3471     } else {
3472     $self->{set_next_char}->($self);
3473     }
3474 wakaba 1.1
3475     redo A;
3476 wakaba 1.18 }
3477 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
3478 wakaba 1.18 ## ISSUE: Redundant "First," in the spec.
3479 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
3480     $self->{next_char} == 0x000A or # LF
3481     $self->{next_char} == 0x000B or # VT
3482     $self->{next_char} == 0x000C or # FF
3483     $self->{next_char} == 0x0020) { # SP
3484 wakaba 1.77
3485 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_NAME_STATE;
3486 wakaba 1.18
3487 wakaba 1.175 pop @{$self->{prev_char}};
3488     unshift @{$self->{prev_char}}, $self->{next_char};
3489    
3490     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3491     $self->{line_prev} = $self->{line};
3492     $self->{column_prev} = $self->{column};
3493     $self->{column}++;
3494     $self->{next_char}
3495     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3496     } else {
3497     $self->{set_next_char}->($self);
3498     }
3499 wakaba 1.18
3500     redo A;
3501 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3502 wakaba 1.77
3503 wakaba 1.59 $self->{state} = DATA_STATE;
3504 wakaba 1.1
3505 wakaba 1.175 pop @{$self->{prev_char}};
3506     unshift @{$self->{prev_char}}, $self->{next_char};
3507    
3508     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3509     $self->{line_prev} = $self->{line};
3510     $self->{column_prev} = $self->{column};
3511     $self->{column}++;
3512     $self->{next_char}
3513     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3514     } else {
3515     $self->{set_next_char}->($self);
3516     }
3517 wakaba 1.1
3518    
3519 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3520 wakaba 1.1
3521     redo A;
3522 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3523 wakaba 1.77
3524 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3525 wakaba 1.59 $self->{state} = DATA_STATE;
3526 wakaba 1.1 ## reconsume
3527    
3528 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3529 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3530 wakaba 1.1
3531     redo A;
3532     } else {
3533 wakaba 1.77
3534 wakaba 1.18 $self->{current_token}->{name}
3535 wakaba 1.76 .= chr ($self->{next_char}); # DOCTYPE
3536 wakaba 1.18 ## Stay in the state
3537 wakaba 1.1
3538 wakaba 1.175 pop @{$self->{prev_char}};
3539     unshift @{$self->{prev_char}}, $self->{next_char};
3540    
3541     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3542     $self->{line_prev} = $self->{line};
3543     $self->{column_prev} = $self->{column};
3544     $self->{column}++;
3545     $self->{next_char}
3546     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3547     } else {
3548     $self->{set_next_char}->($self);
3549     }
3550 wakaba 1.1
3551     redo A;
3552     }
3553 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
3554 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
3555     $self->{next_char} == 0x000A or # LF
3556     $self->{next_char} == 0x000B or # VT
3557     $self->{next_char} == 0x000C or # FF
3558     $self->{next_char} == 0x0020) { # SP
3559 wakaba 1.77
3560 wakaba 1.18 ## Stay in the state
3561 wakaba 1.1
3562 wakaba 1.175 pop @{$self->{prev_char}};
3563     unshift @{$self->{prev_char}}, $self->{next_char};
3564    
3565     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3566     $self->{line_prev} = $self->{line};
3567     $self->{column_prev} = $self->{column};
3568     $self->{column}++;
3569     $self->{next_char}
3570     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3571     } else {
3572     $self->{set_next_char}->($self);
3573     }
3574 wakaba 1.1
3575     redo A;
3576 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3577 wakaba 1.77
3578 wakaba 1.59 $self->{state} = DATA_STATE;
3579 wakaba 1.1
3580 wakaba 1.175 pop @{$self->{prev_char}};
3581     unshift @{$self->{prev_char}}, $self->{next_char};
3582    
3583     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3584     $self->{line_prev} = $self->{line};
3585     $self->{column_prev} = $self->{column};
3586     $self->{column}++;
3587     $self->{next_char}
3588     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3589     } else {
3590     $self->{set_next_char}->($self);
3591     }
3592 wakaba 1.1
3593    
3594     return ($self->{current_token}); # DOCTYPE
3595    
3596     redo A;
3597 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3598 wakaba 1.77
3599 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3600 wakaba 1.59 $self->{state} = DATA_STATE;
3601 wakaba 1.18 ## reconsume
3602    
3603 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3604 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3605    
3606     redo A;
3607 wakaba 1.76 } elsif ($self->{next_char} == 0x0050 or # P
3608     $self->{next_char} == 0x0070) { # p
3609 wakaba 1.162 $self->{state} = PUBLIC_STATE;
3610     $self->{state_keyword} = chr $self->{next_char};
3611 wakaba 1.18
3612 wakaba 1.175 pop @{$self->{prev_char}};
3613     unshift @{$self->{prev_char}}, $self->{next_char};
3614    
3615     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3616     $self->{line_prev} = $self->{line};
3617     $self->{column_prev} = $self->{column};
3618     $self->{column}++;
3619     $self->{next_char}
3620     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3621     } else {
3622     $self->{set_next_char}->($self);
3623     }
3624 wakaba 1.18
3625 wakaba 1.162 redo A;
3626     } elsif ($self->{next_char} == 0x0053 or # S
3627     $self->{next_char} == 0x0073) { # s
3628     $self->{state} = SYSTEM_STATE;
3629     $self->{state_keyword} = chr $self->{next_char};
3630    
3631 wakaba 1.175 pop @{$self->{prev_char}};
3632     unshift @{$self->{prev_char}}, $self->{next_char};
3633    
3634     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3635     $self->{line_prev} = $self->{line};
3636     $self->{column_prev} = $self->{column};
3637     $self->{column}++;
3638     $self->{next_char}
3639     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3640     } else {
3641     $self->{set_next_char}->($self);
3642     }
3643 wakaba 1.18
3644 wakaba 1.162 redo A;
3645 wakaba 1.18 } else {
3646 wakaba 1.162
3647     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name');
3648     $self->{current_token}->{quirks} = 1;
3649 wakaba 1.18
3650 wakaba 1.162 $self->{state} = BOGUS_DOCTYPE_STATE;
3651 wakaba 1.18
3652 wakaba 1.175 pop @{$self->{prev_char}};
3653     unshift @{$self->{prev_char}}, $self->{next_char};
3654    
3655     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3656     $self->{line_prev} = $self->{line};
3657     $self->{column_prev} = $self->{column};
3658     $self->{column}++;
3659     $self->{next_char}
3660     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3661     } else {
3662     $self->{set_next_char}->($self);
3663     }
3664 wakaba 1.18
3665 wakaba 1.162 redo A;
3666 wakaba 1.18 }
3667 wakaba 1.162 } elsif ($self->{state} == PUBLIC_STATE) {
3668     ## ASCII case-insensitive
3669     if ($self->{next_char} == [
3670     undef,
3671     0x0055, # U
3672     0x0042, # B
3673     0x004C, # L
3674     0x0049, # I
3675     ]->[length $self->{state_keyword}] or
3676     $self->{next_char} == [
3677     undef,
3678     0x0075, # u
3679     0x0062, # b
3680     0x006C, # l
3681     0x0069, # i
3682     ]->[length $self->{state_keyword}]) {
3683    
3684     ## Stay in the state.
3685     $self->{state_keyword} .= chr $self->{next_char};
3686    
3687 wakaba 1.175 pop @{$self->{prev_char}};
3688     unshift @{$self->{prev_char}}, $self->{next_char};
3689    
3690     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3691     $self->{line_prev} = $self->{line};
3692     $self->{column_prev} = $self->{column};
3693     $self->{column}++;
3694     $self->{next_char}
3695     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3696     } else {
3697     $self->{set_next_char}->($self);
3698     }
3699 wakaba 1.18
3700 wakaba 1.162 redo A;
3701     } elsif ((length $self->{state_keyword}) == 5 and
3702     ($self->{next_char} == 0x0043 or # C
3703     $self->{next_char} == 0x0063)) { # c
3704    
3705     $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3706    
3707 wakaba 1.175 pop @{$self->{prev_char}};
3708     unshift @{$self->{prev_char}}, $self->{next_char};
3709    
3710     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3711     $self->{line_prev} = $self->{line};
3712     $self->{column_prev} = $self->{column};
3713     $self->{column}++;
3714     $self->{next_char}
3715     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3716     } else {
3717     $self->{set_next_char}->($self);
3718     }
3719 wakaba 1.18
3720 wakaba 1.162 redo A;
3721 wakaba 1.18 } else {
3722 wakaba 1.162
3723     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name',
3724     line => $self->{line_prev},
3725     column => $self->{column_prev} + 1 - length $self->{state_keyword});
3726     $self->{current_token}->{quirks} = 1;
3727    
3728     $self->{state} = BOGUS_DOCTYPE_STATE;
3729     ## Reconsume.
3730     redo A;
3731 wakaba 1.18 }
3732 wakaba 1.162 } elsif ($self->{state} == SYSTEM_STATE) {
3733     ## ASCII case-insensitive
3734     if ($self->{next_char} == [
3735     undef,
3736     0x0059, # Y
3737     0x0053, # S
3738     0x0054, # T
3739     0x0045, # E
3740     ]->[length $self->{state_keyword}] or
3741     $self->{next_char} == [
3742     undef,
3743     0x0079, # y
3744     0x0073, # s
3745     0x0074, # t
3746     0x0065, # e
3747     ]->[length $self->{state_keyword}]) {
3748    
3749     ## Stay in the state.
3750     $self->{state_keyword} .= chr $self->{next_char};
3751    
3752 wakaba 1.175 pop @{$self->{prev_char}};
3753     unshift @{$self->{prev_char}}, $self->{next_char};
3754    
3755     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3756     $self->{line_prev} = $self->{line};
3757     $self->{column_prev} = $self->{column};
3758     $self->{column}++;
3759     $self->{next_char}
3760     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3761     } else {
3762     $self->{set_next_char}->($self);
3763     }
3764 wakaba 1.18
3765 wakaba 1.162 redo A;
3766     } elsif ((length $self->{state_keyword}) == 5 and
3767     ($self->{next_char} == 0x004D or # M
3768     $self->{next_char} == 0x006D)) { # m
3769 wakaba 1.18
3770 wakaba 1.162 $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
3771 wakaba 1.77
3772 wakaba 1.175 pop @{$self->{prev_char}};
3773     unshift @{$self->{prev_char}}, $self->{next_char};
3774    
3775     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3776     $self->{line_prev} = $self->{line};
3777     $self->{column_prev} = $self->{column};
3778     $self->{column}++;
3779     $self->{next_char}
3780     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3781     } else {
3782     $self->{set_next_char}->($self);
3783     }
3784 wakaba 1.18
3785 wakaba 1.162 redo A;
3786     } else {
3787    
3788     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name',
3789     line => $self->{line_prev},
3790     column => $self->{column_prev} + 1 - length $self->{state_keyword});
3791     $self->{current_token}->{quirks} = 1;
3792    
3793     $self->{state} = BOGUS_DOCTYPE_STATE;
3794     ## Reconsume.
3795     redo A;
3796 wakaba 1.18 }
3797 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
3798 wakaba 1.18 if ({
3799     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
3800     #0x000D => 1, # HT, LF, VT, FF, SP, CR
3801 wakaba 1.76 }->{$self->{next_char}}) {
3802 wakaba 1.77
3803 wakaba 1.1 ## Stay in the state
3804    
3805 wakaba 1.175 pop @{$self->{prev_char}};
3806     unshift @{$self->{prev_char}}, $self->{next_char};
3807    
3808     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3809     $self->{line_prev} = $self->{line};
3810     $self->{column_prev} = $self->{column};
3811     $self->{column}++;
3812     $self->{next_char}
3813     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3814     } else {
3815     $self->{set_next_char}->($self);
3816     }
3817 wakaba 1.1
3818     redo A;
3819 wakaba 1.76 } elsif ($self->{next_char} eq 0x0022) { # "
3820 wakaba 1.77
3821 wakaba 1.18 $self->{current_token}->{public_identifier} = ''; # DOCTYPE
3822 wakaba 1.59 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
3823 wakaba 1.18
3824 wakaba 1.175 pop @{$self->{prev_char}};
3825     unshift @{$self->{prev_char}}, $self->{next_char};
3826    
3827     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3828     $self->{line_prev} = $self->{line};
3829     $self->{column_prev} = $self->{column};
3830     $self->{column}++;
3831     $self->{next_char}
3832     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3833     } else {
3834     $self->{set_next_char}->($self);
3835     }
3836 wakaba 1.18
3837     redo A;
3838 wakaba 1.76 } elsif ($self->{next_char} eq 0x0027) { # '
3839 wakaba 1.77
3840 wakaba 1.18 $self->{current_token}->{public_identifier} = ''; # DOCTYPE
3841 wakaba 1.59 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
3842 wakaba 1.18
3843 wakaba 1.175 pop @{$self->{prev_char}};
3844     unshift @{$self->{prev_char}}, $self->{next_char};
3845    
3846     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3847     $self->{line_prev} = $self->{line};
3848     $self->{column_prev} = $self->{column};
3849     $self->{column}++;
3850     $self->{next_char}
3851     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3852     } else {
3853     $self->{set_next_char}->($self);
3854     }
3855 wakaba 1.18
3856     redo A;
3857 wakaba 1.76 } elsif ($self->{next_char} eq 0x003E) { # >
3858 wakaba 1.77
3859 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no PUBLIC literal');
3860 wakaba 1.18
3861 wakaba 1.59 $self->{state} = DATA_STATE;
3862 wakaba 1.18
3863 wakaba 1.175 pop @{$self->{prev_char}};
3864     unshift @{$self->{prev_char}}, $self->{next_char};
3865    
3866     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3867     $self->{line_prev} = $self->{line};
3868     $self->{column_prev} = $self->{column};
3869     $self->{column}++;
3870     $self->{next_char}
3871     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3872     } else {
3873     $self->{set_next_char}->($self);
3874     }
3875 wakaba 1.18
3876    
3877 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3878 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3879    
3880     redo A;
3881 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3882 wakaba 1.77
3883 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3884 wakaba 1.18
3885 wakaba 1.59 $self->{state} = DATA_STATE;
3886 wakaba 1.1 ## reconsume
3887    
3888 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3889 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3890 wakaba 1.1
3891     redo A;
3892     } else {
3893 wakaba 1.77
3894 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after PUBLIC');
3895 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3896 wakaba 1.73
3897 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3898 wakaba 1.18
3899 wakaba 1.175 pop @{$self->{prev_char}};
3900     unshift @{$self->{prev_char}}, $self->{next_char};
3901    
3902     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3903     $self->{line_prev} = $self->{line};
3904     $self->{column_prev} = $self->{column};
3905     $self->{column}++;
3906     $self->{next_char}
3907     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3908     } else {
3909     $self->{set_next_char}->($self);
3910     }
3911 wakaba 1.18
3912     redo A;
3913     }
3914 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
3915 wakaba 1.76 if ($self->{next_char} == 0x0022) { # "
3916 wakaba 1.77
3917 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3918 wakaba 1.18
3919 wakaba 1.175 pop @{$self->{prev_char}};
3920     unshift @{$self->{prev_char}}, $self->{next_char};
3921    
3922     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3923     $self->{line_prev} = $self->{line};
3924     $self->{column_prev} = $self->{column};
3925     $self->{column}++;
3926     $self->{next_char}
3927     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3928     } else {
3929     $self->{set_next_char}->($self);
3930     }
3931 wakaba 1.18
3932     redo A;
3933 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3934 wakaba 1.77
3935 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3936 wakaba 1.70
3937     $self->{state} = DATA_STATE;
3938    
3939 wakaba 1.175 pop @{$self->{prev_char}};
3940     unshift @{$self->{prev_char}}, $self->{next_char};
3941    
3942     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3943     $self->{line_prev} = $self->{line};
3944     $self->{column_prev} = $self->{column};
3945     $self->{column}++;
3946     $self->{next_char}
3947     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3948     } else {
3949     $self->{set_next_char}->($self);
3950     }
3951 wakaba 1.70
3952    
3953 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3954 wakaba 1.70 return ($self->{current_token}); # DOCTYPE
3955    
3956     redo A;
3957 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3958 wakaba 1.77
3959 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3960 wakaba 1.18
3961 wakaba 1.59 $self->{state} = DATA_STATE;
3962 wakaba 1.18 ## reconsume
3963    
3964 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3965 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3966    
3967     redo A;
3968     } else {
3969 wakaba 1.77
3970 wakaba 1.18 $self->{current_token}->{public_identifier} # DOCTYPE
3971 wakaba 1.76 .= chr $self->{next_char};
3972 wakaba 1.169 $self->{read_until}->($self->{current_token}->{public_identifier},
3973     q[">],
3974     length $self->{current_token}->{public_identifier});
3975    
3976 wakaba 1.18 ## Stay in the state
3977    
3978 wakaba 1.175 pop @{$self->{prev_char}};
3979     unshift @{$self->{prev_char}}, $self->{next_char};
3980    
3981     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3982     $self->{line_prev} = $self->{line};
3983     $self->{column_prev} = $self->{column};
3984     $self->{column}++;
3985     $self->{next_char}
3986     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3987     } else {
3988     $self->{set_next_char}->($self);
3989     }
3990 wakaba 1.18
3991     redo A;
3992     }
3993 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
3994 wakaba 1.76 if ($self->{next_char} == 0x0027) { # '
3995 wakaba 1.77
3996 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3997 wakaba 1.18
3998 wakaba 1.175 pop @{$self->{prev_char}};
3999     unshift @{$self->{prev_char}}, $self->{next_char};
4000    
4001     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4002     $self->{line_prev} = $self->{line};
4003     $self->{column_prev} = $self->{column};
4004     $self->{column}++;
4005     $self->{next_char}
4006     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4007     } else {
4008     $self->{set_next_char}->($self);
4009     }
4010 wakaba 1.18
4011     redo A;
4012 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
4013 wakaba 1.77
4014 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
4015 wakaba 1.70
4016     $self->{state} = DATA_STATE;
4017    
4018 wakaba 1.175 pop @{$self->{prev_char}};
4019     unshift @{$self->{prev_char}}, $self->{next_char};
4020    
4021     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4022     $self->{line_prev} = $self->{line};
4023     $self->{column_prev} = $self->{column};
4024     $self->{column}++;
4025     $self->{next_char}
4026     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4027     } else {
4028     $self->{set_next_char}->($self);
4029     }
4030 wakaba 1.70
4031    
4032 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4033 wakaba 1.70 return ($self->{current_token}); # DOCTYPE
4034    
4035     redo A;
4036 wakaba 1.76 } elsif ($self->{next_char} == -1) {
4037 wakaba 1.77
4038 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
4039 wakaba 1.18
4040 wakaba 1.59 $self->{state} = DATA_STATE;
4041 wakaba 1.18 ## reconsume
4042    
4043 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4044 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
4045    
4046     redo A;
4047     } else {
4048 wakaba 1.77
4049 wakaba 1.18 $self->{current_token}->{public_identifier} # DOCTYPE
4050 wakaba 1.76 .= chr $self->{next_char};
4051 wakaba 1.169 $self->{read_until}->($self->{current_token}->{public_identifier},
4052     q['>],
4053     length $self->{current_token}->{public_identifier});
4054    
4055 wakaba 1.18 ## Stay in the state
4056    
4057 wakaba 1.175 pop @{$self->{prev_char}};
4058     unshift @{$self->{prev_char}}, $self->{next_char};
4059    
4060     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4061     $self->{line_prev} = $self->{line};
4062     $self->{column_prev} = $self->{column};
4063     $self->{column}++;
4064     $self->{next_char}
4065     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4066     } else {
4067     $self->{set_next_char}->($self);
4068     }
4069 wakaba 1.18
4070     redo A;
4071     }
4072 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
4073 wakaba 1.18 if ({
4074     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
4075     #0x000D => 1, # HT, LF, VT, FF, SP, CR
4076 wakaba 1.76 }->{$self->{next_char}}) {
4077 wakaba 1.77
4078 wakaba 1.1 ## Stay in the state
4079    
4080 wakaba 1.175 pop @{$self->{prev_char}};
4081     unshift @{$self->{prev_char}}, $self->{next_char};
4082    
4083     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4084     $self->{line_prev} = $self->{line};
4085     $self->{column_prev} = $self->{column};
4086     $self->{column}++;
4087     $self->{next_char}
4088     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4089     } else {
4090     $self->{set_next_char}->($self);
4091     }
4092 wakaba 1.1
4093     redo A;
4094 wakaba 1.76 } elsif ($self->{next_char} == 0x0022) { # "
4095 wakaba 1.77
4096 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
4097 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
4098 wakaba 1.18
4099 wakaba 1.175 pop @{$self->{prev_char}};
4100     unshift @{$self->{prev_char}}, $self->{next_char};
4101    
4102     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4103     $self->{line_prev} = $self->{line};
4104     $self->{column_prev} = $self->{column};
4105     $self->{column}++;
4106     $self->{next_char}
4107     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4108     } else {
4109     $self->{set_next_char}->($self);
4110     }
4111 wakaba 1.18
4112     redo A;
4113 wakaba 1.76 } elsif ($self->{next_char} == 0x0027) { # '
4114 wakaba 1.77
4115 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
4116 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
4117 wakaba 1.18
4118 wakaba 1.175 pop @{$self->{prev_char}};
4119     unshift @{$self->{prev_char}}, $self->{next_char};
4120    
4121     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4122     $self->{line_prev} = $self->{line};
4123     $self->{column_prev} = $self->{column};
4124     $self->{column}++;
4125     $self->{next_char}
4126     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4127     } else {
4128     $self->{set_next_char}->($self);
4129     }
4130 wakaba 1.18
4131     redo A;
4132 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
4133 wakaba 1.77
4134 wakaba 1.59 $self->{state} = DATA_STATE;
4135 wakaba 1.18
4136 wakaba 1.175 pop @{$self->{prev_char}};
4137     unshift @{$self->{prev_char}}, $self->{next_char};
4138    
4139     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4140     $self->{line_prev} = $self->{line};
4141     $self->{column_prev} = $self->{column};
4142     $self->{column}++;
4143     $self->{next_char}
4144     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4145     } else {
4146     $self->{set_next_char}->($self);
4147     }
4148 wakaba 1.18
4149    
4150     return ($self->{current_token}); # DOCTYPE
4151    
4152     redo A;
4153 wakaba 1.76 } elsif ($self->{next_char} == -1) {
4154 wakaba 1.77
4155 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
4156 wakaba 1.18
4157 wakaba 1.59 $self->{state} = DATA_STATE;
4158 wakaba 1.26 ## reconsume
4159 wakaba 1.18
4160 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4161 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
4162    
4163     redo A;
4164     } else {
4165 wakaba 1.77
4166 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after PUBLIC literal');
4167 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4168 wakaba 1.73
4169 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
4170 wakaba 1.18
4171 wakaba 1.175 pop @{$self->{prev_char}};
4172     unshift @{$self->{prev_char}}, $self->{next_char};
4173    
4174     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4175     $self->{line_prev} = $self->{line};
4176     $self->{column_prev} = $self->{column};
4177     $self->{column}++;
4178     $self->{next_char}
4179     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4180     } else {
4181     $self->{set_next_char}->($self);
4182     }
4183 wakaba 1.18
4184     redo A;
4185 wakaba 1.1 }
4186 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
4187 wakaba 1.18 if ({
4188     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
4189     #0x000D => 1, # HT, LF, VT, FF, SP, CR
4190 wakaba 1.76 }->{$self->{next_char}}) {
4191 wakaba 1.77
4192 wakaba 1.1 ## Stay in the state
4193    
4194 wakaba 1.175 pop @{$self->{prev_char}};
4195     unshift @{$self->{prev_char}}, $self->{next_char};
4196    
4197     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4198     $self->{line_prev} = $self->{line};
4199     $self->{column_prev} = $self->{column};
4200     $self->{column}++;
4201     $self->{next_char}
4202     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4203     } else {
4204     $self->{set_next_char}->($self);
4205     }
4206 wakaba 1.1
4207     redo A;
4208 wakaba 1.76 } elsif ($self->{next_char} == 0x0022) { # "
4209 wakaba 1.77
4210 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
4211 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
4212 wakaba 1.18
4213 wakaba 1.175 pop @{$self->{prev_char}};
4214     unshift @{$self->{prev_char}}, $self->{next_char};
4215    
4216     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4217     $self->{line_prev} = $self->{line};
4218     $self->{column_prev} = $self->{column};
4219     $self->{column}++;
4220     $self->{next_char}
4221     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4222     } else {
4223     $self->{set_next_char}->($self);
4224     }
4225 wakaba 1.18
4226     redo A;
4227 wakaba 1.76 } elsif ($self->{next_char} == 0x0027) { # '
4228 wakaba 1.77
4229 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
4230 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
4231 wakaba 1.18
4232 wakaba 1.175 pop @{$self->{prev_char}};
4233     unshift @{$self->{prev_char}}, $self->{next_char};
4234    
4235     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4236     $self->{line_prev} = $self->{line};
4237     $self->{column_prev} = $self->{column};
4238     $self->{column}++;
4239     $self->{next_char}
4240     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4241     } else {
4242     $self->{set_next_char}->($self);
4243     }
4244 wakaba 1.18
4245     redo A;
4246 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
4247 wakaba 1.77
4248 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no SYSTEM literal');
4249 wakaba 1.59 $self->{state} = DATA_STATE;
4250 wakaba 1.1
4251 wakaba 1.175 pop @{$self->{prev_char}};
4252     unshift @{$self->{prev_char}}, $self->{next_char};
4253    
4254     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4255     $self->{line_prev} = $self->{line};
4256     $self->{column_prev} = $self->{column};
4257     $self->{column}++;
4258     $self->{next_char}
4259     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4260     } else {
4261     $self->{set_next_char}->($self);
4262     }
4263 wakaba 1.1
4264    
4265 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4266 wakaba 1.1 return ($self->{current_token}); # DOCTYPE
4267    
4268     redo A;
4269 wakaba 1.76 } elsif ($self->{next_char} == -1) {
4270 wakaba 1.77
4271 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
4272 wakaba 1.18
4273 wakaba 1.59 $self->{state} = DATA_STATE;
4274 wakaba 1.26 ## reconsume
4275 wakaba 1.18
4276 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4277 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
4278    
4279     redo A;
4280     } else {
4281 wakaba 1.77
4282 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after SYSTEM');
4283 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4284 wakaba 1.73
4285 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
4286 wakaba 1.18
4287 wakaba 1.175 pop @{$self->{prev_char}};
4288     unshift @{$self->{prev_char}}, $self->{next_char};
4289    
4290     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4291     $self->{line_prev} = $self->{line};
4292     $self->{column_prev} = $self->{column};
4293     $self->{column}++;
4294     $self->{next_char}
4295     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4296     } else {
4297     $self->{set_next_char}->($self);
4298     }
4299 wakaba 1.18
4300     redo A;
4301     }
4302 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
4303 wakaba 1.76 if ($self->{next_char} == 0x0022) { # "
4304 wakaba 1.77
4305 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
4306 wakaba 1.18
4307 wakaba 1.175 pop @{$self->{prev_char}};
4308     unshift @{$self->{prev_char}}, $self->{next_char};
4309    
4310     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4311     $self->{line_prev} = $self->{line};
4312     $self->{column_prev} = $self->{column};
4313     $self->{column}++;
4314     $self->{next_char}
4315     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4316     } else {
4317     $self->{set_next_char}->($self);
4318     }
4319 wakaba 1.18
4320     redo A;
4321 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
4322 wakaba 1.77
4323 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4324 wakaba 1.70
4325     $self->{state} = DATA_STATE;
4326    
4327 wakaba 1.175 pop @{$self->{prev_char}};
4328     unshift @{$self->{prev_char}}, $self->{next_char};
4329    
4330     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4331     $self->{line_prev} = $self->{line};
4332     $self->{column_prev} = $self->{column};
4333     $self->{column}++;
4334     $self->{next_char}
4335     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4336     } else {
4337     $self->{set_next_char}->($self);
4338     }
4339 wakaba 1.70
4340    
4341 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4342 wakaba 1.70 return ($self->{current_token}); # DOCTYPE
4343    
4344     redo A;
4345 wakaba 1.76 } elsif ($self->{next_char} == -1) {
4346 wakaba 1.77
4347 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4348 wakaba 1.18
4349 wakaba 1.59 $self->{state} = DATA_STATE;
4350 wakaba 1.1 ## reconsume
4351    
4352 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4353 wakaba 1.1 return ($self->{current_token}); # DOCTYPE
4354    
4355     redo A;
4356     } else {
4357 wakaba 1.77
4358 wakaba 1.18 $self->{current_token}->{system_identifier} # DOCTYPE
4359 wakaba 1.76 .= chr $self->{next_char};
4360 wakaba 1.169 $self->{read_until}->($self->{current_token}->{system_identifier},
4361     q[">],
4362     length $self->{current_token}->{system_identifier});
4363    
4364 wakaba 1.18 ## Stay in the state
4365    
4366 wakaba 1.175 pop @{$self->{prev_char}};
4367     unshift @{$self->{prev_char}}, $self->{next_char};
4368    
4369     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4370     $self->{line_prev} = $self->{line};
4371     $self->{column_prev} = $self->{column};
4372     $self->{column}++;
4373     $self->{next_char}
4374     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4375     } else {
4376     $self->{set_next_char}->($self);
4377     }
4378 wakaba 1.18
4379     redo A;
4380     }
4381 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
4382 wakaba 1.76 if ($self->{next_char} == 0x0027) { # '
4383 wakaba 1.77
4384 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
4385 wakaba 1.18
4386 wakaba 1.175 pop @{$self->{prev_char}};
4387     unshift @{$self->{prev_char}}, $self->{next_char};
4388    
4389     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4390     $self->{line_prev} = $self->{line};
4391     $self->{column_prev} = $self->{column};
4392     $self->{column}++;
4393     $self->{next_char}
4394     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4395     } else {
4396     $self->{set_next_char}->($self);
4397     }
4398 wakaba 1.18
4399     redo A;
4400 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
4401 wakaba 1.77
4402 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4403 wakaba 1.70
4404     $self->{state} = DATA_STATE;
4405    
4406 wakaba 1.175 pop @{$self->{prev_char}};
4407     unshift @{$self->{prev_char}}, $self->{next_char};
4408    
4409     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4410     $self->{line_prev} = $self->{line};
4411     $self->{column_prev} = $self->{column};
4412     $self->{column}++;
4413     $self->{next_char}
4414     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4415     } else {
4416     $self->{set_next_char}->($self);
4417     }
4418 wakaba 1.70
4419    
4420 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4421 wakaba 1.70 return ($self->{current_token}); # DOCTYPE
4422    
4423     redo A;
4424 wakaba 1.76 } elsif ($self->{next_char} == -1) {
4425 wakaba 1.77
4426 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4427 wakaba 1.18
4428 wakaba 1.59 $self->{state} = DATA_STATE;
4429 wakaba 1.18 ## reconsume
4430    
4431 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4432 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
4433    
4434     redo A;
4435     } else {
4436 wakaba 1.77
4437 wakaba 1.18 $self->{current_token}->{system_identifier} # DOCTYPE
4438 wakaba 1.76 .= chr $self->{next_char};
4439 wakaba 1.169 $self->{read_until}->($self->{current_token}->{system_identifier},
4440     q['>],
4441     length $self->{current_token}->{system_identifier});
4442    
4443 wakaba 1.18 ## Stay in the state
4444    
4445 wakaba 1.175 pop @{$self->{prev_char}};
4446     unshift @{$self->{prev_char}}, $self->{next_char};
4447    
4448     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4449     $self->{line_prev} = $self->{line};
4450     $self->{column_prev} = $self->{column};
4451     $self->{column}++;
4452     $self->{next_char}
4453     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4454     } else {
4455     $self->{set_next_char}->($self);
4456     }
4457 wakaba 1.18
4458     redo A;
4459     }
4460 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
4461 wakaba 1.18 if ({
4462     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
4463     #0x000D => 1, # HT, LF, VT, FF, SP, CR
4464 wakaba 1.76 }->{$self->{next_char}}) {
4465 wakaba 1.77
4466 wakaba 1.18 ## Stay in the state
4467    
4468 wakaba 1.175 pop @{$self->{prev_char}};
4469     unshift @{$self->{prev_char}}, $self->{next_char};
4470    
4471     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4472     $self->{line_prev} = $self->{line};
4473     $self->{column_prev} = $self->{column};
4474     $self->{column}++;
4475     $self->{next_char}
4476     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4477     } else {
4478     $self->{set_next_char}->($self);
4479     }
4480 wakaba 1.18
4481     redo A;
4482 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
4483 wakaba 1.77
4484 wakaba 1.59 $self->{state} = DATA_STATE;
4485 wakaba 1.18
4486 wakaba 1.175 pop @{$self->{prev_char}};
4487     unshift @{$self->{prev_char}}, $self->{next_char};
4488    
4489     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4490     $self->{line_prev} = $self->{line};
4491     $self->{column_prev} = $self->{column};
4492     $self->{column}++;
4493     $self->{next_char}
4494     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4495     } else {
4496     $self->{set_next_char}->($self);
4497     }
4498 wakaba 1.18
4499    
4500     return ($self->{current_token}); # DOCTYPE
4501    
4502     redo A;
4503 wakaba 1.76 } elsif ($self->{next_char} == -1) {
4504 wakaba 1.77
4505 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
4506 wakaba 1.59 $self->{state} = DATA_STATE;
4507 wakaba 1.26 ## reconsume
4508 wakaba 1.18
4509 wakaba 1.75 $self->{current_token}->{quirks} = 1;
4510 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
4511    
4512     redo A;
4513     } else {
4514 wakaba 1.77
4515 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after SYSTEM literal');
4516 wakaba 1.75 #$self->{current_token}->{quirks} = 1;
4517 wakaba 1.73
4518 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
4519 wakaba 1.1
4520 wakaba 1.175 pop @{$self->{prev_char}};
4521     unshift @{$self->{prev_char}}, $self->{next_char};
4522    
4523     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4524     $self->{line_prev} = $self->{line};
4525     $self->{column_prev} = $self->{column};
4526     $self->{column}++;
4527     $self->{next_char}
4528     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4529     } else {
4530     $self->{set_next_char}->($self);
4531     }
4532 wakaba 1.1
4533     redo A;
4534     }
4535 wakaba 1.59 } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
4536 wakaba 1.76 if ($self->{next_char} == 0x003E) { # >
4537 wakaba 1.77
4538 wakaba 1.59 $self->{state} = DATA_STATE;
4539 wakaba 1.1
4540 wakaba 1.175 pop @{$self->{prev_char}};
4541     unshift @{$self->{prev_char}}, $self->{next_char};
4542    
4543     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4544     $self->{line_prev} = $self->{line};
4545     $self->{column_prev} = $self->{column};
4546     $self->{column}++;
4547     $self->{next_char}
4548     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4549     } else {
4550     $self->{set_next_char}->($self);
4551     }
4552 wakaba 1.1
4553    
4554     return ($self->{current_token}); # DOCTYPE
4555    
4556     redo A;
4557 wakaba 1.76 } elsif ($self->{next_char} == -1) {
4558 wakaba 1.77
4559 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
4560 wakaba 1.59 $self->{state} = DATA_STATE;
4561 wakaba 1.1 ## reconsume
4562    
4563     return ($self->{current_token}); # DOCTYPE
4564    
4565     redo A;
4566     } else {
4567 wakaba 1.77
4568 wakaba 1.169 my $s = '';
4569     $self->{read_until}->($s, q[>], 0);
4570    
4571 wakaba 1.1 ## Stay in the state
4572    
4573 wakaba 1.175 pop @{$self->{prev_char}};
4574     unshift @{$self->{prev_char}}, $self->{next_char};
4575    
4576     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4577     $self->{line_prev} = $self->{line};
4578     $self->{column_prev} = $self->{column};
4579     $self->{column}++;
4580     $self->{next_char}
4581     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4582     } else {
4583     $self->{set_next_char}->($self);
4584     }
4585 wakaba 1.1
4586     redo A;
4587     }
4588 wakaba 1.161 } elsif ($self->{state} == CDATA_SECTION_STATE) {
4589     ## NOTE: "CDATA section state" in the state is jointly implemented
4590     ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
4591     ## and |CDATA_SECTION_MSE2_STATE|.
4592 wakaba 1.124
4593 wakaba 1.161 if ($self->{next_char} == 0x005D) { # ]
4594    
4595     $self->{state} = CDATA_SECTION_MSE1_STATE;
4596    
4597 wakaba 1.175 pop @{$self->{prev_char}};
4598     unshift @{$self->{prev_char}}, $self->{next_char};
4599    
4600     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4601     $self->{line_prev} = $self->{line};
4602     $self->{column_prev} = $self->{column};
4603     $self->{column}++;
4604     $self->{next_char}
4605     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4606     } else {
4607     $self->{set_next_char}->($self);
4608     }
4609 wakaba 1.161
4610     redo A;
4611     } elsif ($self->{next_char} == -1) {
4612     $self->{state} = DATA_STATE;
4613    
4614 wakaba 1.175 pop @{$self->{prev_char}};
4615     unshift @{$self->{prev_char}}, $self->{next_char};
4616    
4617     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4618     $self->{line_prev} = $self->{line};
4619     $self->{column_prev} = $self->{column};
4620     $self->{column}++;
4621     $self->{next_char}
4622     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4623     } else {
4624     $self->{set_next_char}->($self);
4625     }
4626 wakaba 1.124
4627 wakaba 1.161 if (length $self->{current_token}->{data}) { # character
4628    
4629     return ($self->{current_token}); # character
4630     } else {
4631    
4632     ## No token to emit. $self->{current_token} is discarded.
4633     }
4634     redo A;
4635     } else {
4636    
4637     $self->{current_token}->{data} .= chr $self->{next_char};
4638 wakaba 1.169 $self->{read_until}->($self->{current_token}->{data},
4639     q<]>,
4640     length $self->{current_token}->{data});
4641    
4642 wakaba 1.161 ## Stay in the state.
4643    
4644 wakaba 1.175 pop @{$self->{prev_char}};
4645     unshift @{$self->{prev_char}}, $self->{next_char};
4646    
4647     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4648     $self->{line_prev} = $self->{line};
4649     $self->{column_prev} = $self->{column};
4650     $self->{column}++;
4651     $self->{next_char}
4652     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4653     } else {
4654     $self->{set_next_char}->($self);
4655     }
4656 wakaba 1.124
4657 wakaba 1.161 redo A;
4658     }
4659    
4660     ## ISSUE: "text tokens" in spec.
4661     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
4662     if ($self->{next_char} == 0x005D) { # ]
4663    
4664     $self->{state} = CDATA_SECTION_MSE2_STATE;
4665    
4666 wakaba 1.175 pop @{$self->{prev_char}};
4667     unshift @{$self->{prev_char}}, $self->{next_char};
4668    
4669     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4670     $self->{line_prev} = $self->{line};
4671     $self->{column_prev} = $self->{column};
4672     $self->{column}++;
4673     $self->{next_char}
4674     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4675     } else {
4676     $self->{set_next_char}->($self);
4677     }
4678 wakaba 1.124
4679 wakaba 1.161 redo A;
4680     } else {
4681    
4682     $self->{current_token}->{data} .= ']';
4683     $self->{state} = CDATA_SECTION_STATE;
4684     ## Reconsume.
4685     redo A;
4686     }
4687     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
4688     if ($self->{next_char} == 0x003E) { # >
4689     $self->{state} = DATA_STATE;
4690    
4691 wakaba 1.175 pop @{$self->{prev_char}};
4692     unshift @{$self->{prev_char}}, $self->{next_char};
4693    
4694     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4695     $self->{line_prev} = $self->{line};
4696     $self->{column_prev} = $self->{column};
4697     $self->{column}++;
4698     $self->{next_char}
4699     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4700     } else {
4701     $self->{set_next_char}->($self);
4702     }
4703 wakaba 1.124
4704 wakaba 1.161 if (length $self->{current_token}->{data}) { # character
4705    
4706     return ($self->{current_token}); # character
4707 wakaba 1.124 } else {
4708    
4709 wakaba 1.161 ## No token to emit. $self->{current_token} is discarded.
4710 wakaba 1.124 }
4711 wakaba 1.161 redo A;
4712     } elsif ($self->{next_char} == 0x005D) { # ]
4713     # character
4714     $self->{current_token}->{data} .= ']'; ## Add first "]" of "]]]".
4715     ## Stay in the state.
4716 wakaba 1.124
4717 wakaba 1.175 pop @{$self->{prev_char}};
4718     unshift @{$self->{prev_char}}, $self->{next_char};
4719    
4720     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4721     $self->{line_prev} = $self->{line};
4722     $self->{column_prev} = $self->{column};
4723     $self->{column}++;
4724     $self->{next_char}
4725     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4726     } else {
4727     $self->{set_next_char}->($self);
4728     }
4729 wakaba 1.124
4730 wakaba 1.161 redo A;
4731 wakaba 1.124 } else {
4732    
4733 wakaba 1.161 $self->{current_token}->{data} .= ']]'; # character
4734     $self->{state} = CDATA_SECTION_STATE;
4735     ## Reconsume.
4736     redo A;
4737 wakaba 1.124 }
4738 wakaba 1.163 } elsif ($self->{state} == ENTITY_STATE) {
4739 wakaba 1.164 if ({
4740     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
4741     0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, &
4742     $self->{entity_additional} => 1,
4743 wakaba 1.76 }->{$self->{next_char}}) {
4744 wakaba 1.164
4745     ## Don't consume
4746     ## No error
4747     ## Return nothing.
4748     #
4749     } elsif ($self->{next_char} == 0x0023) { # #
4750 wakaba 1.166
4751 wakaba 1.164 $self->{state} = ENTITY_HASH_STATE;
4752     $self->{state_keyword} = '#';
4753    
4754 wakaba 1.175 pop @{$self->{prev_char}};
4755     unshift @{$self->{prev_char}}, $self->{next_char};
4756    
4757     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4758     $self->{line_prev} = $self->{line};
4759     $self->{column_prev} = $self->{column};
4760     $self->{column}++;
4761     $self->{next_char}
4762     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4763     } else {
4764     $self->{set_next_char}->($self);
4765     }
4766 wakaba 1.1
4767 wakaba 1.164 redo A;
4768     } elsif ((0x0041 <= $self->{next_char} and
4769     $self->{next_char} <= 0x005A) or # A..Z
4770     (0x0061 <= $self->{next_char} and
4771     $self->{next_char} <= 0x007A)) { # a..z
4772 wakaba 1.166
4773 wakaba 1.164 require Whatpm::_NamedEntityList;
4774     $self->{state} = ENTITY_NAME_STATE;
4775     $self->{state_keyword} = chr $self->{next_char};
4776     $self->{entity__value} = $self->{state_keyword};
4777     $self->{entity__match} = 0;
4778 wakaba 1.1
4779 wakaba 1.175 pop @{$self->{prev_char}};
4780     unshift @{$self->{prev_char}}, $self->{next_char};
4781    
4782     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4783     $self->{line_prev} = $self->{line};
4784     $self->{column_prev} = $self->{column};
4785     $self->{column}++;
4786     $self->{next_char}
4787     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4788     } else {
4789     $self->{set_next_char}->($self);
4790     }
4791 wakaba 1.1
4792 wakaba 1.164 redo A;
4793     } else {
4794    
4795     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare ero');
4796     ## Return nothing.
4797     #
4798     }
4799    
4800     ## NOTE: No character is consumed by the "consume a character
4801     ## reference" algorithm. In other word, there is an "&" character
4802     ## that does not introduce a character reference, which would be
4803     ## appended to the parent element or the attribute value in later
4804     ## process of the tokenizer.
4805    
4806 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4807 wakaba 1.166
4808 wakaba 1.165 $self->{state} = $self->{prev_state};
4809 wakaba 1.164 ## Reconsume.
4810     return ({type => CHARACTER_TOKEN, data => '&',
4811     line => $self->{line_prev},
4812     column => $self->{column_prev},
4813     });
4814     redo A;
4815 wakaba 1.165 } else {
4816 wakaba 1.166
4817 wakaba 1.165 $self->{current_attribute}->{value} .= '&';
4818     $self->{state} = $self->{prev_state};
4819     ## Reconsume.
4820     redo A;
4821 wakaba 1.164 }
4822     } elsif ($self->{state} == ENTITY_HASH_STATE) {
4823     if ($self->{next_char} == 0x0078 or # x
4824     $self->{next_char} == 0x0058) { # X
4825 wakaba 1.166
4826 wakaba 1.164 $self->{state} = HEXREF_X_STATE;
4827     $self->{state_keyword} .= chr $self->{next_char};
4828    
4829 wakaba 1.175 pop @{$self->{prev_char}};
4830     unshift @{$self->{prev_char}}, $self->{next_char};
4831    
4832     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4833     $self->{line_prev} = $self->{line};
4834     $self->{column_prev} = $self->{column};
4835     $self->{column}++;
4836     $self->{next_char}
4837     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4838     } else {
4839     $self->{set_next_char}->($self);
4840     }
4841 wakaba 1.1
4842 wakaba 1.163 redo A;
4843 wakaba 1.164 } elsif (0x0030 <= $self->{next_char} and
4844     $self->{next_char} <= 0x0039) { # 0..9
4845 wakaba 1.166
4846 wakaba 1.164 $self->{state} = NCR_NUM_STATE;
4847     $self->{state_keyword} = $self->{next_char} - 0x0030;
4848    
4849 wakaba 1.175 pop @{$self->{prev_char}};
4850     unshift @{$self->{prev_char}}, $self->{next_char};
4851    
4852     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4853     $self->{line_prev} = $self->{line};
4854     $self->{column_prev} = $self->{column};
4855     $self->{column}++;
4856     $self->{next_char}
4857     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4858     } else {
4859     $self->{set_next_char}->($self);
4860     }
4861 wakaba 1.1
4862 wakaba 1.164 redo A;
4863     } else {
4864     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare nero',
4865     line => $self->{line_prev},
4866     column => $self->{column_prev} - 1);
4867    
4868     ## NOTE: According to the spec algorithm, nothing is returned,
4869     ## and then "&#" is appended to the parent element or the attribute
4870     ## value in the later processing.
4871    
4872 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4873 wakaba 1.166
4874 wakaba 1.165 $self->{state} = $self->{prev_state};
4875 wakaba 1.164 ## Reconsume.
4876     return ({type => CHARACTER_TOKEN,
4877     data => '&#',
4878     line => $self->{line_prev},
4879     column => $self->{column_prev} - 1,
4880     });
4881     redo A;
4882 wakaba 1.165 } else {
4883 wakaba 1.166
4884 wakaba 1.165 $self->{current_attribute}->{value} .= '&#';
4885     $self->{state} = $self->{prev_state};
4886     ## Reconsume.
4887     redo A;
4888 wakaba 1.164 }
4889     }
4890     } elsif ($self->{state} == NCR_NUM_STATE) {
4891     if (0x0030 <= $self->{next_char} and
4892     $self->{next_char} <= 0x0039) { # 0..9
4893 wakaba 1.78
4894 wakaba 1.164 $self->{state_keyword} *= 10;
4895     $self->{state_keyword} += $self->{next_char} - 0x0030;
4896 wakaba 1.1
4897 wakaba 1.164 ## Stay in the state.
4898 wakaba 1.1
4899 wakaba 1.175 pop @{$self->{prev_char}};
4900     unshift @{$self->{prev_char}}, $self->{next_char};
4901    
4902     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4903     $self->{line_prev} = $self->{line};
4904     $self->{column_prev} = $self->{column};
4905     $self->{column}++;
4906     $self->{next_char}
4907     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4908     } else {
4909     $self->{set_next_char}->($self);
4910     }
4911 wakaba 1.1
4912 wakaba 1.164 redo A;
4913     } elsif ($self->{next_char} == 0x003B) { # ;
4914 wakaba 1.1
4915 wakaba 1.78
4916 wakaba 1.175 pop @{$self->{prev_char}};
4917     unshift @{$self->{prev_char}}, $self->{next_char};
4918    
4919     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4920     $self->{line_prev} = $self->{line};
4921     $self->{column_prev} = $self->{column};
4922     $self->{column}++;
4923     $self->{next_char}
4924     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4925     } else {
4926     $self->{set_next_char}->($self);
4927     }
4928 wakaba 1.1
4929 wakaba 1.164 #
4930 wakaba 1.1 } else {
4931 wakaba 1.78
4932 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc');
4933     ## Reconsume.
4934     #
4935 wakaba 1.1 }
4936    
4937 wakaba 1.164 my $code = $self->{state_keyword};
4938     my $l = $self->{line_prev};
4939     my $c = $self->{column_prev};
4940 wakaba 1.26 if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
4941 wakaba 1.78
4942 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4943     text => (sprintf 'U+%04X', $code),
4944     line => $l, column => $c);
4945 wakaba 1.26 $code = 0xFFFD;
4946     } elsif ($code > 0x10FFFF) {
4947 wakaba 1.78
4948 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4949     text => (sprintf 'U-%08X', $code),
4950     line => $l, column => $c);
4951 wakaba 1.26 $code = 0xFFFD;
4952     } elsif ($code == 0x000D) {
4953 wakaba 1.78
4954 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'CR character reference',
4955     line => $l, column => $c);
4956 wakaba 1.26 $code = 0x000A;
4957 wakaba 1.4 } elsif (0x80 <= $code and $code <= 0x9F) {
4958 wakaba 1.78
4959 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'C1 character reference',
4960     text => (sprintf 'U+%04X', $code),
4961     line => $l, column => $c);
4962 wakaba 1.4 $code = $c1_entity_char->{$code};
4963 wakaba 1.1 }
4964 wakaba 1.164
4965 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4966 wakaba 1.166
4967 wakaba 1.165 $self->{state} = $self->{prev_state};
4968 wakaba 1.164 ## Reconsume.
4969 wakaba 1.165 return ({type => CHARACTER_TOKEN, data => chr $code,
4970     line => $l, column => $c,
4971     });
4972 wakaba 1.164 redo A;
4973     } else {
4974 wakaba 1.166
4975 wakaba 1.165 $self->{current_attribute}->{value} .= chr $code;
4976     $self->{current_attribute}->{has_reference} = 1;
4977     $self->{state} = $self->{prev_state};
4978 wakaba 1.164 ## Reconsume.
4979     redo A;
4980     }
4981     } elsif ($self->{state} == HEXREF_X_STATE) {
4982     if ((0x0030 <= $self->{next_char} and $self->{next_char} <= 0x0039) or
4983     (0x0041 <= $self->{next_char} and $self->{next_char} <= 0x0046) or
4984     (0x0061 <= $self->{next_char} and $self->{next_char} <= 0x0066)) {
4985     # 0..9, A..F, a..f
4986 wakaba 1.166
4987 wakaba 1.164 $self->{state} = HEXREF_HEX_STATE;
4988     $self->{state_keyword} = 0;
4989     ## Reconsume.
4990     redo A;
4991     } else {
4992     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare hcro',
4993     line => $self->{line_prev},
4994     column => $self->{column_prev} - 2);
4995    
4996     ## NOTE: According to the spec algorithm, nothing is returned,
4997     ## and then "&#" followed by "X" or "x" is appended to the parent
4998     ## element or the attribute value in the later processing.
4999    
5000 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
5001 wakaba 1.166
5002 wakaba 1.165 $self->{state} = $self->{prev_state};
5003 wakaba 1.164 ## Reconsume.
5004     return ({type => CHARACTER_TOKEN,
5005     data => '&' . $self->{state_keyword},
5006     line => $self->{line_prev},
5007     column => $self->{column_prev} - length $self->{state_keyword},
5008     });
5009     redo A;
5010 wakaba 1.165 } else {
5011 wakaba 1.166
5012 wakaba 1.165 $self->{current_attribute}->{value} .= '&' . $self->{state_keyword};
5013     $self->{state} = $self->{prev_state};
5014     ## Reconsume.
5015     redo A;
5016 wakaba 1.164 }
5017     }
5018     } elsif ($self->{state} == HEXREF_HEX_STATE) {
5019     if (0x0030 <= $self->{next_char} and $self->{next_char} <= 0x0039) {
5020     # 0..9
5021    
5022     $self->{state_keyword} *= 0x10;
5023     $self->{state_keyword} += $self->{next_char} - 0x0030;
5024     ## Stay in the state.
5025    
5026 wakaba 1.175 pop @{$self->{prev_char}};
5027     unshift @{$self->{prev_char}}, $self->{next_char};
5028    
5029     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
5030     $self->{line_prev} = $self->{line};
5031     $self->{column_prev} = $self->{column};
5032     $self->{column}++;
5033     $self->{next_char}
5034     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
5035     } else {
5036     $self->{set_next_char}->($self);
5037     }
5038 wakaba 1.164
5039     redo A;
5040     } elsif (0x0061 <= $self->{next_char} and
5041     $self->{next_char} <= 0x0066) { # a..f
5042    
5043     $self->{state_keyword} *= 0x10;
5044     $self->{state_keyword} += $self->{next_char} - 0x0060 + 9;
5045     ## Stay in the state.
5046    
5047 wakaba 1.175 pop @{$self->{prev_char}};
5048     unshift @{$self->{prev_char}}, $self->{next_char};
5049    
5050     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
5051     $self->{line_prev} = $self->{line};
5052     $self->{column_prev} = $self->{column};
5053     $self->{column}++;
5054     $self->{next_char}
5055     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
5056     } else {
5057     $self->{set_next_char}->($self);
5058     }
5059 wakaba 1.1
5060 wakaba 1.164 redo A;
5061     } elsif (0x0041 <= $self->{next_char} and
5062     $self->{next_char} <= 0x0046) { # A..F
5063    
5064     $self->{state_keyword} *= 0x10;
5065     $self->{state_keyword} += $self->{next_char} - 0x0040 + 9;
5066     ## Stay in the state.
5067    
5068 wakaba 1.175 pop @{$self->{prev_char}};
5069     unshift @{$self->{prev_char}}, $self->{next_char};
5070    
5071     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
5072     $self->{line_prev} = $self->{line};
5073     $self->{column_prev} = $self->{column};
5074     $self->{column}++;
5075     $self->{next_char}
5076     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
5077     } else {
5078     $self->{set_next_char}->($self);
5079     }
5080 wakaba 1.16
5081 wakaba 1.164 redo A;
5082     } elsif ($self->{next_char} == 0x003B) { # ;
5083    
5084    
5085 wakaba 1.175 pop @{$self->{prev_char}};
5086     unshift @{$self->{prev_char}}, $self->{next_char};
5087    
5088     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
5089     $self->{line_prev} = $self->{line};
5090     $self->{column_prev} = $self->{column};
5091     $self->{column}++;
5092     $self->{next_char}
5093     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
5094     } else {
5095     $self->{set_next_char}->($self);
5096     }
5097 wakaba 1.37
5098 wakaba 1.164 #
5099 wakaba 1.1 } else {
5100 wakaba 1.78
5101 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc',
5102     line => $self->{line},
5103     column => $self->{column});
5104     ## Reconsume.
5105     #
5106     }
5107    
5108     my $code = $self->{state_keyword};
5109     my $l = $self->{line_prev};
5110     my $c = $self->{column_prev};
5111     if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
5112    
5113     $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
5114     text => (sprintf 'U+%04X', $code),
5115     line => $l, column => $c);
5116     $code = 0xFFFD;
5117     } elsif ($code > 0x10FFFF) {
5118 wakaba 1.37
5119 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
5120     text => (sprintf 'U-%08X', $code),
5121     line => $l, column => $c);
5122     $code = 0xFFFD;
5123     } elsif ($code == 0x000D) {
5124    
5125     $self->{parse_error}->(level => $self->{level}->{must}, type => 'CR character reference', line => $l, column => $c);
5126     $code = 0x000A;
5127     } elsif (0x80 <= $code and $code <= 0x9F) {
5128    
5129     $self->{parse_error}->(level => $self->{level}->{must}, type => 'C1 character reference', text => (sprintf 'U+%04X', $code), line => $l, column => $c);
5130     $code = $c1_entity_char->{$code};
5131     }
5132    
5133 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
5134 wakaba 1.166
5135 wakaba 1.165 $self->{state} = $self->{prev_state};
5136 wakaba 1.164 ## Reconsume.
5137 wakaba 1.165 return ({type => CHARACTER_TOKEN, data => chr $code,
5138     line => $l, column => $c,
5139     });
5140 wakaba 1.164 redo A;
5141     } else {
5142 wakaba 1.166
5143 wakaba 1.165 $self->{current_attribute}->{value} .= chr $code;
5144     $self->{current_attribute}->{has_reference} = 1;
5145     $self->{state} = $self->{prev_state};
5146 wakaba 1.164 ## Reconsume.
5147     redo A;
5148     }
5149     } elsif ($self->{state} == ENTITY_NAME_STATE) {
5150     if (length $self->{state_keyword} < 30 and
5151     ## NOTE: Some number greater than the maximum length of entity name
5152     ((0x0041 <= $self->{next_char} and # a
5153     $self->{next_char} <= 0x005A) or # x
5154     (0x0061 <= $self->{next_char} and # a
5155     $self->{next_char} <= 0x007A) or # z
5156     (0x0030 <= $self->{next_char} and # 0
5157     $self->{next_char} <= 0x0039) or # 9
5158     $self->{next_char} == 0x003B)) { # ;
5159     our $EntityChar;
5160     $self->{state_keyword} .= chr $self->{next_char};
5161     if (defined $EntityChar->{$self->{state_keyword}}) {
5162     if ($self->{next_char} == 0x003B) { # ;
5163    
5164     $self->{entity__value} = $EntityChar->{$self->{state_keyword}};
5165     $self->{entity__match} = 1;
5166    
5167 wakaba 1.175 pop @{$self->{prev_char}};
5168     unshift @{$self->{prev_char}}, $self->{next_char};
5169    
5170     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
5171     $self->{line_prev} = $self->{line};
5172     $self->{column_prev} = $self->{column};
5173     $self->{column}++;
5174     $self->{next_char}
5175     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
5176     } else {
5177     $self->{set_next_char}->($self);
5178     }
5179 wakaba 1.1
5180 wakaba 1.164 #
5181     } else {
5182    
5183     $self->{entity__value} = $EntityChar->{$self->{state_keyword}};
5184     $self->{entity__match} = -1;
5185     ## Stay in the state.
5186    
5187 wakaba 1.175 pop @{$self->{prev_char}};
5188     unshift @{$self->{prev_char}}, $self->{next_char};
5189    
5190     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
5191     $self->{line_prev} = $self->{line};
5192     $self->{column_prev} = $self->{column};
5193     $self->{column}++;
5194     $self->{next_char}
5195     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
5196     } else {
5197     $self->{set_next_char}->($self);
5198     }
5199 wakaba 1.164
5200     redo A;
5201     }
5202     } else {
5203    
5204     $self->{entity__value} .= chr $self->{next_char};
5205     $self->{entity__match} *= 2;
5206     ## Stay in the state.
5207    
5208 wakaba 1.175 pop @{$self->{prev_char}};
5209     unshift @{$self->{prev_char}}, $self->{next_char};
5210    
5211     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
5212     $self->{line_prev} = $self->{line};
5213     $self->{column_prev} = $self->{column};
5214     $self->{column}++;
5215     $self->{next_char}
5216     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
5217     } else {
5218     $self->{set_next_char}->($self);
5219     }
5220 wakaba 1.164
5221     redo A;
5222     }
5223     }
5224    
5225     my $data;
5226     my $has_ref;
5227     if ($self->{entity__match} > 0) {
5228    
5229     $data = $self->{entity__value};
5230     $has_ref = 1;
5231     #
5232     } elsif ($self->{entity__match} < 0) {
5233     $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc');
5234 wakaba 1.165 if ($self->{prev_state} != DATA_STATE and # in attribute
5235     $self->{entity__match} < -1) {
5236 wakaba 1.164
5237     $data = '&' . $self->{state_keyword};
5238     #
5239     } else {
5240    
5241     $data = $self->{entity__value};
5242     $has_ref = 1;
5243     #
5244     }
5245     } else {
5246 wakaba 1.78
5247 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare ero',
5248     line => $self->{line_prev},
5249 wakaba 1.172 column => $self->{column_prev} - length $self->{state_keyword});
5250 wakaba 1.164 $data = '&' . $self->{state_keyword};
5251     #
5252     }
5253    
5254     ## NOTE: In these cases, when a character reference is found,
5255     ## it is consumed and a character token is returned, or, otherwise,
5256     ## nothing is consumed and returned, according to the spec algorithm.
5257     ## In this implementation, anything that has been examined by the
5258     ## tokenizer is appended to the parent element or the attribute value
5259     ## as string, either literal string when no character reference or
5260     ## entity-replaced string otherwise, in this stage, since any characters
5261     ## that would not be consumed are appended in the data state or in an
5262     ## appropriate attribute value state anyway.
5263    
5264 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
5265 wakaba 1.166
5266 wakaba 1.165 $self->{state} = $self->{prev_state};
5267 wakaba 1.164 ## Reconsume.
5268     return ({type => CHARACTER_TOKEN,
5269 wakaba 1.165 data => $data,
5270 wakaba 1.164 line => $self->{line_prev},
5271     column => $self->{column_prev} + 1 - length $self->{state_keyword},
5272     });
5273 wakaba 1.163 redo A;
5274 wakaba 1.165 } else {
5275 wakaba 1.166
5276 wakaba 1.165 $self->{current_attribute}->{value} .= $data;
5277     $self->{current_attribute}->{has_reference} = 1 if $has_ref;
5278     $self->{state} = $self->{prev_state};
5279     ## Reconsume.
5280     redo A;
5281 wakaba 1.37 }
5282 wakaba 1.1 } else {
5283 wakaba 1.163 die "$0: $self->{state}: Unknown state";
5284     }
5285     } # A
5286    
5287     die "$0: _get_next_token: unexpected case";
5288     } # _get_next_token
5289 wakaba 1.1
5290     sub _initialize_tree_constructor ($) {
5291     my $self = shift;
5292     ## NOTE: $self->{document} MUST be specified before this method is called
5293     $self->{document}->strict_error_checking (0);
5294     ## TODO: Turn mutation events off # MUST
5295     ## TODO: Turn loose Document option (manakai extension) on
5296 wakaba 1.18 $self->{document}->manakai_is_html (1); # MUST
5297 wakaba 1.151 $self->{document}->set_user_data (manakai_source_line => 1);
5298     $self->{document}->set_user_data (manakai_source_column => 1);
5299 wakaba 1.1 } # _initialize_tree_constructor
5300    
5301     sub _terminate_tree_constructor ($) {
5302     my $self = shift;
5303     $self->{document}->strict_error_checking (1);
5304     ## TODO: Turn mutation events on
5305     } # _terminate_tree_constructor
5306    
5307     ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?
5308    
5309 wakaba 1.3 { # tree construction stage
5310     my $token;
5311    
5312 wakaba 1.1 sub _construct_tree ($) {
5313     my ($self) = @_;
5314    
5315     ## When an interactive UA render the $self->{document} available
5316     ## to the user, or when it begin accepting user input, are
5317     ## not defined.
5318    
5319     ## Append a character: collect it and all subsequent consecutive
5320     ## characters and insert one Text node whose data is concatenation
5321     ## of all those characters. # MUST
5322    
5323     $token = $self->_get_next_token;
5324    
5325 wakaba 1.3 undef $self->{form_element};
5326     undef $self->{head_element};
5327     $self->{open_elements} = [];
5328     undef $self->{inner_html_node};
5329    
5330 wakaba 1.84 ## NOTE: The "initial" insertion mode.
5331 wakaba 1.3 $self->_tree_construction_initial; # MUST
5332 wakaba 1.84
5333 wakaba 1.85 ## NOTE: The "before html" insertion mode.
5334 wakaba 1.3 $self->_tree_construction_root_element;
5335 wakaba 1.84 $self->{insertion_mode} = BEFORE_HEAD_IM;
5336    
5337     ## NOTE: The "before head" insertion mode and so on.
5338 wakaba 1.3 $self->_tree_construction_main;
5339     } # _construct_tree
5340    
5341     sub _tree_construction_initial ($) {
5342     my $self = shift;
5343 wakaba 1.84
5344     ## NOTE: "initial" insertion mode
5345    
5346 wakaba 1.18 INITIAL: {
5347 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
5348 wakaba 1.18 ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
5349     ## error, switch to a conformance checking mode for another
5350     ## language.
5351     my $doctype_name = $token->{name};
5352     $doctype_name = '' unless defined $doctype_name;
5353 wakaba 1.155 $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
5354 wakaba 1.18 if (not defined $token->{name} or # <!DOCTYPE>
5355     defined $token->{system_identifier}) {
5356 wakaba 1.79
5357 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
5358 wakaba 1.18 } elsif ($doctype_name ne 'HTML') {
5359 wakaba 1.79
5360 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
5361 wakaba 1.155 } elsif (defined $token->{public_identifier}) {
5362     if ($token->{public_identifier} eq 'XSLT-compat') {
5363    
5364     $self->{parse_error}->(level => $self->{level}->{must}, type => 'XSLT-compat', token => $token,
5365     level => $self->{level}->{should});
5366     } else {
5367     $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
5368     }
5369 wakaba 1.79 } else {
5370    
5371 wakaba 1.155 #
5372 wakaba 1.18 }
5373    
5374     my $doctype = $self->{document}->create_document_type_definition
5375     ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
5376 wakaba 1.120 ## NOTE: Default value for both |public_id| and |system_id| attributes
5377     ## are empty strings, so that we don't set any value in missing cases.
5378 wakaba 1.18 $doctype->public_id ($token->{public_identifier})
5379     if defined $token->{public_identifier};
5380     $doctype->system_id ($token->{system_identifier})
5381     if defined $token->{system_identifier};
5382     ## NOTE: Other DocumentType attributes are null or empty lists.
5383     ## ISSUE: internalSubset = null??
5384     $self->{document}->append_child ($doctype);
5385    
5386 wakaba 1.75 if ($token->{quirks} or $doctype_name ne 'HTML') {
5387 wakaba 1.79
5388 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5389     } elsif (defined $token->{public_identifier}) {
5390     my $pubid = $token->{public_identifier};
5391     $pubid =~ tr/a-z/A-z/;
5392 wakaba 1.140 my $prefix = [
5393     "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
5394     "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
5395     "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
5396     "-//IETF//DTD HTML 2.0 LEVEL 1//",
5397     "-//IETF//DTD HTML 2.0 LEVEL 2//",
5398     "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
5399     "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
5400     "-//IETF//DTD HTML 2.0 STRICT//",
5401     "-//IETF//DTD HTML 2.0//",
5402     "-//IETF//DTD HTML 2.1E//",
5403     "-//IETF//DTD HTML 3.0//",
5404     "-//IETF//DTD HTML 3.2 FINAL//",
5405     "-//IETF//DTD HTML 3.2//",
5406     "-//IETF//DTD HTML 3//",
5407     "-//IETF//DTD HTML LEVEL 0//",
5408     "-//IETF//DTD HTML LEVEL 1//",
5409     "-//IETF//DTD HTML LEVEL 2//",
5410     "-//IETF//DTD HTML LEVEL 3//",
5411     "-//IETF//DTD HTML STRICT LEVEL 0//",
5412     "-//IETF//DTD HTML STRICT LEVEL 1//",
5413     "-//IETF//DTD HTML STRICT LEVEL 2//",
5414     "-//IETF//DTD HTML STRICT LEVEL 3//",
5415     "-//IETF//DTD HTML STRICT//",
5416     "-//IETF//DTD HTML//",
5417     "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
5418     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
5419     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
5420     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
5421     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
5422     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
5423     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
5424     "-//NETSCAPE COMM. CORP.//DTD HTML//",
5425     "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
5426     "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
5427     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
5428     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
5429     "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
5430     "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
5431     "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
5432     "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
5433     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
5434     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
5435     "-//W3C//DTD HTML 3 1995-03-24//",
5436     "-//W3C//DTD HTML 3.2 DRAFT//",
5437     "-//W3C//DTD HTML 3.2 FINAL//",
5438     "-//W3C//DTD HTML 3.2//",
5439     "-//W3C//DTD HTML 3.2S DRAFT//",
5440     "-//W3C//DTD HTML 4.0 FRAMESET//",
5441     "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
5442     "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
5443     "-//W3C//DTD HTML EXPERIMENTAL 970421//",
5444     "-//W3C//DTD W3 HTML//",
5445     "-//W3O//DTD W3 HTML 3.0//",
5446     "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
5447     "-//WEBTECHS//DTD MOZILLA HTML//",
5448     ]; # $prefix
5449     my $match;
5450     for (@$prefix) {
5451     if (substr ($prefix, 0, length $_) eq $_) {
5452     $match = 1;
5453     last;
5454     }
5455     }
5456     if ($match or
5457     $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
5458     $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
5459     $pubid eq "HTML") {
5460 wakaba 1.79
5461 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5462 wakaba 1.140 } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
5463     $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
5464 wakaba 1.18 if (defined $token->{system_identifier}) {
5465 wakaba 1.79
5466 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5467     } else {
5468 wakaba 1.79
5469 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
5470 wakaba 1.3 }
5471 wakaba 1.140 } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
5472     $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
5473 wakaba 1.79
5474 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
5475 wakaba 1.79 } else {
5476    
5477 wakaba 1.18 }
5478 wakaba 1.79 } else {
5479    
5480 wakaba 1.18 }
5481     if (defined $token->{system_identifier}) {
5482     my $sysid = $token->{system_identifier};
5483     $sysid =~ tr/A-Z/a-z/;
5484     if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
5485 wakaba 1.140 ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
5486     ## marked as quirks.
5487 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5488 wakaba 1.79
5489     } else {
5490    
5491 wakaba 1.18 }
5492 wakaba 1.79 } else {
5493    
5494 wakaba 1.18 }
5495    
5496 wakaba 1.85 ## Go to the "before html" insertion mode.
5497 wakaba 1.18 $token = $self->_get_next_token;
5498     return;
5499     } elsif ({
5500 wakaba 1.57 START_TAG_TOKEN, 1,
5501     END_TAG_TOKEN, 1,
5502     END_OF_FILE_TOKEN, 1,
5503 wakaba 1.18 }->{$token->{type}}) {
5504 wakaba 1.79
5505 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE', token => $token);
5506 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5507 wakaba 1.85 ## Go to the "before html" insertion mode.
5508 wakaba 1.18 ## reprocess
5509 wakaba 1.122
5510 wakaba 1.18 return;
5511 wakaba 1.57 } elsif ($token->{type} == CHARACTER_TOKEN) {
5512 wakaba 1.18 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
5513     ## Ignore the token
5514 wakaba 1.26
5515 wakaba 1.18 unless (length $token->{data}) {
5516 wakaba 1.79
5517 wakaba 1.84 ## Stay in the insertion mode.
5518 wakaba 1.18 $token = $self->_get_next_token;
5519     redo INITIAL;
5520 wakaba 1.79 } else {
5521    
5522 wakaba 1.3 }
5523 wakaba 1.79 } else {
5524    
5525 wakaba 1.3 }
5526 wakaba 1.18
5527 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE', token => $token);
5528 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5529 wakaba 1.85 ## Go to the "before html" insertion mode.
5530 wakaba 1.18 ## reprocess
5531     return;
5532 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
5533 wakaba 1.79
5534 wakaba 1.18 my $comment = $self->{document}->create_comment ($token->{data});
5535     $self->{document}->append_child ($comment);
5536    
5537 wakaba 1.84 ## Stay in the insertion mode.
5538 wakaba 1.18 $token = $self->_get_next_token;
5539     redo INITIAL;
5540     } else {
5541 wakaba 1.57 die "$0: $token->{type}: Unknown token type";
5542 wakaba 1.18 }
5543     } # INITIAL
5544 wakaba 1.79
5545     die "$0: _tree_construction_initial: This should be never reached";
5546 wakaba 1.3 } # _tree_construction_initial
5547    
5548     sub _tree_construction_root_element ($) {
5549     my $self = shift;
5550 wakaba 1.84
5551 wakaba 1.85 ## NOTE: "before html" insertion mode.
5552 wakaba 1.3
5553     B: {
5554 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
5555 wakaba 1.79
5556 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in html:#DOCTYPE', token => $token);
5557 wakaba 1.3 ## Ignore the token
5558 wakaba 1.84 ## Stay in the insertion mode.
5559 wakaba 1.3 $token = $self->_get_next_token;
5560     redo B;
5561 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
5562 wakaba 1.79
5563 wakaba 1.3 my $comment = $self->{document}->create_comment ($token->{data});
5564     $self->{document}->append_child ($comment);
5565 wakaba 1.84 ## Stay in the insertion mode.
5566 wakaba 1.3 $token = $self->_get_next_token;
5567     redo B;
5568 wakaba 1.57 } elsif ($token->{type} == CHARACTER_TOKEN) {
5569 wakaba 1.26 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
5570     ## Ignore the token.
5571    
5572 wakaba 1.3 unless (length $token->{data}) {
5573 wakaba 1.79
5574 wakaba 1.84 ## Stay in the insertion mode.
5575 wakaba 1.3 $token = $self->_get_next_token;
5576     redo B;
5577 wakaba 1.79 } else {
5578    
5579 wakaba 1.3 }
5580 wakaba 1.79 } else {
5581    
5582 wakaba 1.3 }
5583 wakaba 1.63
5584     $self->{application_cache_selection}->(undef);
5585    
5586     #
5587     } elsif ($token->{type} == START_TAG_TOKEN) {
5588 wakaba 1.84 if ($token->{tag_name} eq 'html') {
5589     my $root_element;
5590 wakaba 1.79
5591 wakaba 1.84 $root_element = $self->{document}->create_element_ns
5592 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5593 wakaba 1.84
5594     for my $attr_name (keys %{ $token->{attributes}}) {
5595 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5596 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5597 wakaba 1.117 $attr->value ($attr_t->{value});
5598     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5599     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5600     $root_element->set_attribute_node_ns ($attr);
5601 wakaba 1.84 }
5602    
5603 wakaba 1.114 $root_element->set_user_data (manakai_source_line => $token->{line})
5604     if defined $token->{line};
5605     $root_element->set_user_data (manakai_source_column => $token->{column})
5606     if defined $token->{column};
5607    
5608 wakaba 1.84 $self->{document}->append_child ($root_element);
5609 wakaba 1.121 push @{$self->{open_elements}},
5610     [$root_element, $el_category->{html}];
5611 wakaba 1.84
5612     if ($token->{attributes}->{manifest}) {
5613    
5614     $self->{application_cache_selection}
5615     ->($token->{attributes}->{manifest}->{value});
5616 wakaba 1.116 ## ISSUE: Spec is unclear on relative references.
5617     ## According to Hixie (#whatwg 2008-03-19), it should be
5618     ## resolved against the base URI of the document in HTML
5619     ## or xml:base of the element in XHTML.
5620 wakaba 1.84 } else {
5621    
5622     $self->{application_cache_selection}->(undef);
5623     }
5624    
5625 wakaba 1.122
5626    
5627 wakaba 1.84 $token = $self->_get_next_token;
5628     return; ## Go to the "before head" insertion mode.
5629 wakaba 1.63 } else {
5630 wakaba 1.79
5631 wakaba 1.84 #
5632 wakaba 1.63 }
5633 wakaba 1.3 } elsif ({
5634 wakaba 1.57 END_TAG_TOKEN, 1,
5635     END_OF_FILE_TOKEN, 1,
5636 wakaba 1.3 }->{$token->{type}}) {
5637 wakaba 1.79
5638 wakaba 1.3 #
5639     } else {
5640 wakaba 1.57 die "$0: $token->{type}: Unknown token type";
5641 wakaba 1.3 }
5642 wakaba 1.63
5643 wakaba 1.123 my $root_element;
5644    
5645 wakaba 1.3 $root_element = $self->{document}->create_element_ns
5646 wakaba 1.128 ($HTML_NS, [undef, 'html']);
5647 wakaba 1.3
5648 wakaba 1.114 $root_element->set_user_data (manakai_source_line => $token->{line})
5649     if defined $token->{line};
5650     $root_element->set_user_data (manakai_source_column => $token->{column})
5651     if defined $token->{column};
5652    
5653 wakaba 1.84 $self->{document}->append_child ($root_element);
5654 wakaba 1.121 push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
5655 wakaba 1.84
5656     $self->{application_cache_selection}->(undef);
5657    
5658     ## NOTE: Reprocess the token.
5659 wakaba 1.122
5660 wakaba 1.84 return; ## Go to the "before head" insertion mode.
5661    
5662     ## ISSUE: There is an issue in the spec
5663 wakaba 1.3 } # B
5664 wakaba 1.79
5665     die "$0: _tree_construction_root_element: This should never be reached";
5666 wakaba 1.3 } # _tree_construction_root_element
5667    
5668     sub _reset_insertion_mode ($) {
5669     my $self = shift;
5670    
5671     ## Step 1
5672     my $last;
5673    
5674     ## Step 2
5675     my $i = -1;
5676     my $node = $self->{open_elements}->[$i];
5677    
5678     ## Step 3
5679     S3: {
5680 wakaba 1.29 if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
5681     $last = 1;
5682     if (defined $self->{inner_html_node}) {
5683 wakaba 1.137
5684     $node = $self->{inner_html_node};
5685     } else {
5686     die "_reset_insertion_mode: t27";
5687 wakaba 1.3 }
5688     }
5689 wakaba 1.137
5690     ## Step 4..14
5691     my $new_mode;
5692     if ($node->[1] & FOREIGN_EL) {
5693    
5694     ## NOTE: Strictly spaking, the line below only applies to MathML and
5695     ## SVG elements. Currently the HTML syntax supports only MathML and
5696     ## SVG elements as foreigners.
5697 wakaba 1.145 $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
5698 wakaba 1.137 } elsif ($node->[1] & TABLE_CELL_EL) {
5699     if ($last) {
5700    
5701     #
5702     } else {
5703    
5704     $new_mode = IN_CELL_IM;
5705     }
5706     } else {
5707    
5708     $new_mode = {
5709 wakaba 1.56 select => IN_SELECT_IM,
5710 wakaba 1.83 ## NOTE: |option| and |optgroup| do not set
5711     ## insertion mode to "in select" by themselves.
5712 wakaba 1.56 tr => IN_ROW_IM,
5713     tbody => IN_TABLE_BODY_IM,
5714     thead => IN_TABLE_BODY_IM,
5715     tfoot => IN_TABLE_BODY_IM,
5716     caption => IN_CAPTION_IM,
5717     colgroup => IN_COLUMN_GROUP_IM,
5718     table => IN_TABLE_IM,
5719     head => IN_BODY_IM, # not in head!
5720     body => IN_BODY_IM,
5721     frameset => IN_FRAMESET_IM,
5722 wakaba 1.121 }->{$node->[0]->manakai_local_name};
5723 wakaba 1.137 }
5724     $self->{insertion_mode} = $new_mode and return if defined $new_mode;
5725 wakaba 1.3
5726 wakaba 1.123 ## Step 15
5727 wakaba 1.121 if ($node->[1] & HTML_EL) {
5728 wakaba 1.3 unless (defined $self->{head_element}) {
5729 wakaba 1.79
5730 wakaba 1.56 $self->{insertion_mode} = BEFORE_HEAD_IM;
5731 wakaba 1.3 } else {
5732 wakaba 1.81 ## ISSUE: Can this state be reached?
5733 wakaba 1.79
5734 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
5735 wakaba 1.3 }
5736     return;
5737 wakaba 1.79 } else {
5738    
5739 wakaba 1.3 }
5740    
5741 wakaba 1.123 ## Step 16
5742 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM and return if $last;
5743 wakaba 1.3
5744 wakaba 1.123 ## Step 17
5745 wakaba 1.3 $i--;
5746     $node = $self->{open_elements}->[$i];
5747    
5748 wakaba 1.123 ## Step 18
5749 wakaba 1.3 redo S3;
5750     } # S3
5751 wakaba 1.79
5752     die "$0: _reset_insertion_mode: This line should never be reached";
5753 wakaba 1.3 } # _reset_insertion_mode
5754    
5755     sub _tree_construction_main ($) {
5756     my $self = shift;
5757    
5758 wakaba 1.1 my $active_formatting_elements = [];
5759    
5760     my $reconstruct_active_formatting_elements = sub { # MUST
5761     my $insert = shift;
5762    
5763     ## Step 1
5764     return unless @$active_formatting_elements;
5765    
5766     ## Step 3
5767     my $i = -1;
5768     my $entry = $active_formatting_elements->[$i];
5769    
5770     ## Step 2
5771     return if $entry->[0] eq '#marker';
5772 wakaba 1.3 for (@{$self->{open_elements}}) {
5773 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
5774 wakaba 1.79
5775 wakaba 1.1 return;
5776     }
5777     }
5778    
5779     S4: {
5780     ## Step 4
5781     last S4 if $active_formatting_elements->[0]->[0] eq $entry->[0];
5782    
5783     ## Step 5
5784     $i--;
5785     $entry = $active_formatting_elements->[$i];
5786    
5787     ## Step 6
5788     if ($entry->[0] eq '#marker') {
5789 wakaba 1.79
5790 wakaba 1.1 #
5791     } else {
5792     my $in_open_elements;
5793 wakaba 1.3 OE: for (@{$self->{open_elements}}) {
5794 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
5795 wakaba 1.79
5796 wakaba 1.1 $in_open_elements = 1;
5797     last OE;
5798     }
5799     }
5800     if ($in_open_elements) {
5801 wakaba 1.79
5802 wakaba 1.1 #
5803     } else {
5804 wakaba 1.81 ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
5805 wakaba 1.79
5806 wakaba 1.1 redo S4;
5807     }
5808     }
5809    
5810     ## Step 7
5811     $i++;
5812     $entry = $active_formatting_elements->[$i];
5813     } # S4
5814    
5815     S7: {
5816     ## Step 8
5817     my $clone = [$entry->[0]->clone_node (0), $entry->[1]];
5818    
5819     ## Step 9
5820     $insert->($clone->[0]);
5821 wakaba 1.3 push @{$self->{open_elements}}, $clone;
5822 wakaba 1.1
5823     ## Step 10
5824 wakaba 1.3 $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
5825 wakaba 1.1
5826     ## Step 11
5827     unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
5828 wakaba 1.79
5829 wakaba 1.1 ## Step 7'
5830     $i++;
5831     $entry = $active_formatting_elements->[$i];
5832    
5833     redo S7;
5834     }
5835 wakaba 1.79
5836    
5837 wakaba 1.1 } # S7
5838     }; # $reconstruct_active_formatting_elements
5839    
5840     my $clear_up_to_marker = sub {
5841     for (reverse 0..$#$active_formatting_elements) {
5842     if ($active_formatting_elements->[$_]->[0] eq '#marker') {
5843 wakaba 1.79
5844 wakaba 1.1 splice @$active_formatting_elements, $_;
5845     return;
5846     }
5847     }
5848 wakaba 1.79
5849    
5850 wakaba 1.1 }; # $clear_up_to_marker
5851    
5852 wakaba 1.96 my $insert;
5853    
5854     my $parse_rcdata = sub ($) {
5855     my ($content_model_flag) = @_;
5856 wakaba 1.25
5857     ## Step 1
5858     my $start_tag_name = $token->{tag_name};
5859     my $el;
5860    
5861     $el = $self->{document}->create_element_ns
5862 wakaba 1.128 ($HTML_NS, [undef, $start_tag_name]);
5863 wakaba 1.1
5864 wakaba 1.6 for my $attr_name (keys %{ $token->{attributes}}) {
5865 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5866 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5867 wakaba 1.117 $attr->value ($attr_t->{value});
5868     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5869     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5870     $el->set_attribute_node_ns ($attr);
5871 wakaba 1.6 }
5872    
5873 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5874     if defined $token->{line};
5875     $el->set_user_data (manakai_source_column => $token->{column})
5876     if defined $token->{column};
5877    
5878 wakaba 1.25
5879     ## Step 2
5880 wakaba 1.96 $insert->($el);
5881 wakaba 1.25
5882     ## Step 3
5883 wakaba 1.41 $self->{content_model} = $content_model_flag; # CDATA or RCDATA
5884 wakaba 1.13 delete $self->{escape}; # MUST
5885 wakaba 1.25
5886     ## Step 4
5887 wakaba 1.1 my $text = '';
5888 wakaba 1.122
5889 wakaba 1.1 $token = $self->_get_next_token;
5890 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
5891 wakaba 1.79
5892 wakaba 1.1 $text .= $token->{data};
5893     $token = $self->_get_next_token;
5894 wakaba 1.25 }
5895    
5896     ## Step 5
5897 wakaba 1.1 if (length $text) {
5898 wakaba 1.79
5899 wakaba 1.25 my $text = $self->{document}->create_text_node ($text);
5900     $el->append_child ($text);
5901 wakaba 1.1 }
5902 wakaba 1.25
5903     ## Step 6
5904 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL;
5905 wakaba 1.25
5906     ## Step 7
5907 wakaba 1.79 if ($token->{type} == END_TAG_TOKEN and
5908     $token->{tag_name} eq $start_tag_name) {
5909    
5910 wakaba 1.1 ## Ignore the token
5911     } else {
5912 wakaba 1.96 ## NOTE: An end-of-file token.
5913     if ($content_model_flag == CDATA_CONTENT_MODEL) {
5914    
5915 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in CDATA:#eof', token => $token);
5916 wakaba 1.96 } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
5917    
5918 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in RCDATA:#eof', token => $token);
5919 wakaba 1.96 } else {
5920     die "$0: $content_model_flag in parse_rcdata";
5921     }
5922 wakaba 1.1 }
5923     $token = $self->_get_next_token;
5924 wakaba 1.25 }; # $parse_rcdata
5925 wakaba 1.1
5926 wakaba 1.96 my $script_start_tag = sub () {
5927 wakaba 1.1 my $script_el;
5928    
5929     $script_el = $self->{document}->create_element_ns
5930 wakaba 1.128 ($HTML_NS, [undef, 'script']);
5931 wakaba 1.1
5932     for my $attr_name (keys %{ $token->{attributes}}) {
5933 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5934 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5935 wakaba 1.117 $attr->value ($attr_t->{value});
5936     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5937     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5938     $script_el->set_attribute_node_ns ($attr);
5939 wakaba 1.1 }
5940    
5941 wakaba 1.114 $script_el->set_user_data (manakai_source_line => $token->{line})
5942     if defined $token->{line};
5943     $script_el->set_user_data (manakai_source_column => $token->{column})
5944     if defined $token->{column};
5945    
5946 wakaba 1.1 ## TODO: mark as "parser-inserted"
5947    
5948 wakaba 1.41 $self->{content_model} = CDATA_CONTENT_MODEL;
5949 wakaba 1.13 delete $self->{escape}; # MUST
5950 wakaba 1.1
5951     my $text = '';
5952 wakaba 1.122
5953 wakaba 1.1 $token = $self->_get_next_token;
5954 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) {
5955 wakaba 1.79
5956 wakaba 1.1 $text .= $token->{data};
5957     $token = $self->_get_next_token;
5958     } # stop if non-character token or tokenizer stops tokenising
5959     if (length $text) {
5960 wakaba 1.79
5961 wakaba 1.1 $script_el->manakai_append_text ($text);
5962     }
5963    
5964 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL;
5965 wakaba 1.1
5966 wakaba 1.57 if ($token->{type} == END_TAG_TOKEN and
5967 wakaba 1.1 $token->{tag_name} eq 'script') {
5968 wakaba 1.79
5969 wakaba 1.1 ## Ignore the token
5970     } else {
5971 wakaba 1.79
5972 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in CDATA:#eof', token => $token);
5973 wakaba 1.1 ## ISSUE: And ignore?
5974     ## TODO: mark as "already executed"
5975     }
5976    
5977 wakaba 1.3 if (defined $self->{inner_html_node}) {
5978 wakaba 1.79
5979 wakaba 1.3 ## TODO: mark as "already executed"
5980     } else {
5981 wakaba 1.79
5982 wakaba 1.1 ## TODO: $old_insertion_point = current insertion point
5983     ## TODO: insertion point = just before the next input character
5984 wakaba 1.25
5985     $insert->($script_el);
5986 wakaba 1.1
5987     ## TODO: insertion point = $old_insertion_point (might be "undefined")
5988    
5989     ## TODO: if there is a script that will execute as soon as the parser resume, then...
5990     }
5991    
5992     $token = $self->_get_next_token;
5993     }; # $script_start_tag
5994    
5995 wakaba 1.102 ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
5996     ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
5997     my $open_tables = [[$self->{open_elements}->[0]->[0]]];
5998    
5999 wakaba 1.1 my $formatting_end_tag = sub {
6000 wakaba 1.111 my $end_tag_token = shift;
6001     my $tag_name = $end_tag_token->{tag_name};
6002 wakaba 1.1
6003 wakaba 1.102 ## NOTE: The adoption agency algorithm (AAA).
6004    
6005 wakaba 1.1 FET: {
6006     ## Step 1
6007     my $formatting_element;
6008     my $formatting_element_i_in_active;
6009     AFE: for (reverse 0..$#$active_formatting_elements) {
6010 wakaba 1.121 if ($active_formatting_elements->[$_]->[0] eq '#marker') {
6011    
6012     last AFE;
6013     } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
6014     eq $tag_name) {
6015 wakaba 1.79
6016 wakaba 1.1 $formatting_element = $active_formatting_elements->[$_];
6017     $formatting_element_i_in_active = $_;
6018     last AFE;
6019     }
6020     } # AFE
6021     unless (defined $formatting_element) {
6022 wakaba 1.79
6023 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
6024 wakaba 1.1 ## Ignore the token
6025     $token = $self->_get_next_token;
6026     return;
6027     }
6028     ## has an element in scope
6029     my $in_scope = 1;
6030     my $formatting_element_i_in_open;
6031 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6032     my $node = $self->{open_elements}->[$_];
6033 wakaba 1.1 if ($node->[0] eq $formatting_element->[0]) {
6034     if ($in_scope) {
6035 wakaba 1.79
6036 wakaba 1.1 $formatting_element_i_in_open = $_;
6037     last INSCOPE;
6038     } else { # in open elements but not in scope
6039 wakaba 1.79
6040 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6041     text => $token->{tag_name},
6042 wakaba 1.111 token => $end_tag_token);
6043 wakaba 1.1 ## Ignore the token
6044     $token = $self->_get_next_token;
6045     return;
6046     }
6047 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
6048 wakaba 1.79
6049 wakaba 1.1 $in_scope = 0;
6050     }
6051     } # INSCOPE
6052     unless (defined $formatting_element_i_in_open) {
6053 wakaba 1.79
6054 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6055     text => $token->{tag_name},
6056 wakaba 1.111 token => $end_tag_token);
6057 wakaba 1.1 pop @$active_formatting_elements; # $formatting_element
6058     $token = $self->_get_next_token; ## TODO: ok?
6059     return;
6060     }
6061 wakaba 1.3 if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
6062 wakaba 1.79
6063 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
6064     text => $self->{open_elements}->[-1]->[0]
6065 wakaba 1.120 ->manakai_local_name,
6066 wakaba 1.111 token => $end_tag_token);
6067 wakaba 1.1 }
6068    
6069     ## Step 2
6070     my $furthest_block;
6071     my $furthest_block_i_in_open;
6072 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
6073     my $node = $self->{open_elements}->[$_];
6074 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
6075 wakaba 1.1 #not $phrasing_category->{$node->[1]} and
6076 wakaba 1.121 ($node->[1] & SPECIAL_EL or
6077     $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
6078 wakaba 1.79
6079 wakaba 1.1 $furthest_block = $node;
6080     $furthest_block_i_in_open = $_;
6081     } elsif ($node->[0] eq $formatting_element->[0]) {
6082 wakaba 1.79
6083 wakaba 1.1 last OE;
6084     }
6085     } # OE
6086    
6087     ## Step 3
6088     unless (defined $furthest_block) { # MUST
6089 wakaba 1.79
6090 wakaba 1.3 splice @{$self->{open_elements}}, $formatting_element_i_in_open;
6091 wakaba 1.1 splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
6092     $token = $self->_get_next_token;
6093     return;
6094     }
6095    
6096     ## Step 4
6097 wakaba 1.3 my $common_ancestor_node = $self->{open_elements}->[$formatting_element_i_in_open - 1];
6098 wakaba 1.1
6099     ## Step 5
6100     my $furthest_block_parent = $furthest_block->[0]->parent_node;
6101     if (defined $furthest_block_parent) {
6102 wakaba 1.79
6103 wakaba 1.1 $furthest_block_parent->remove_child ($furthest_block->[0]);
6104     }
6105    
6106     ## Step 6
6107     my $bookmark_prev_el
6108     = $active_formatting_elements->[$formatting_element_i_in_active - 1]
6109     ->[0];
6110    
6111     ## Step 7
6112     my $node = $furthest_block;
6113     my $node_i_in_open = $furthest_block_i_in_open;
6114     my $last_node = $furthest_block;
6115     S7: {
6116     ## Step 1
6117     $node_i_in_open--;
6118 wakaba 1.3 $node = $self->{open_elements}->[$node_i_in_open];
6119 wakaba 1.1
6120     ## Step 2
6121     my $node_i_in_active;
6122     S7S2: {
6123     for (reverse 0..$#$active_formatting_elements) {
6124     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
6125 wakaba 1.79
6126 wakaba 1.1 $node_i_in_active = $_;
6127     last S7S2;
6128     }
6129     }
6130 wakaba 1.3 splice @{$self->{open_elements}}, $node_i_in_open, 1;
6131 wakaba 1.1 redo S7;
6132     } # S7S2
6133    
6134     ## Step 3
6135     last S7 if $node->[0] eq $formatting_element->[0];
6136    
6137     ## Step 4
6138     if ($last_node->[0] eq $furthest_block->[0]) {
6139 wakaba 1.79
6140 wakaba 1.1 $bookmark_prev_el = $node->[0];
6141     }
6142    
6143     ## Step 5
6144     if ($node->[0]->has_child_nodes ()) {
6145 wakaba 1.79
6146 wakaba 1.1 my $clone = [$node->[0]->clone_node (0), $node->[1]];
6147     $active_formatting_elements->[$node_i_in_active] = $clone;
6148 wakaba 1.3 $self->{open_elements}->[$node_i_in_open] = $clone;
6149 wakaba 1.1 $node = $clone;
6150     }
6151    
6152     ## Step 6
6153     $node->[0]->append_child ($last_node->[0]);
6154    
6155     ## Step 7
6156     $last_node = $node;
6157    
6158     ## Step 8
6159     redo S7;
6160     } # S7
6161    
6162     ## Step 8
6163 wakaba 1.121 if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
6164 wakaba 1.102 my $foster_parent_element;
6165     my $next_sibling;
6166 wakaba 1.121 OE: for (reverse 0..$#{$self->{open_elements}}) {
6167     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
6168 wakaba 1.102 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
6169     if (defined $parent and $parent->node_type == 1) {
6170    
6171     $foster_parent_element = $parent;
6172     $next_sibling = $self->{open_elements}->[$_]->[0];
6173     } else {
6174    
6175     $foster_parent_element
6176     = $self->{open_elements}->[$_ - 1]->[0];
6177     }
6178     last OE;
6179     }
6180     } # OE
6181     $foster_parent_element = $self->{open_elements}->[0]->[0]
6182     unless defined $foster_parent_element;
6183     $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
6184     $open_tables->[-1]->[1] = 1; # tainted
6185     } else {
6186    
6187     $common_ancestor_node->[0]->append_child ($last_node->[0]);
6188     }
6189 wakaba 1.1
6190     ## Step 9
6191     my $clone = [$formatting_element->[0]->clone_node (0),
6192     $formatting_element->[1]];
6193    
6194     ## Step 10
6195     my @cn = @{$furthest_block->[0]->child_nodes};
6196     $clone->[0]->append_child ($_) for @cn;
6197    
6198     ## Step 11
6199     $furthest_block->[0]->append_child ($clone->[0]);
6200    
6201     ## Step 12
6202     my $i;
6203     AFE: for (reverse 0..$#$active_formatting_elements) {
6204     if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
6205 wakaba 1.79
6206 wakaba 1.1 splice @$active_formatting_elements, $_, 1;
6207     $i-- and last AFE if defined $i;
6208     } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
6209 wakaba 1.79
6210 wakaba 1.1 $i = $_;
6211     }
6212     } # AFE
6213     splice @$active_formatting_elements, $i + 1, 0, $clone;
6214    
6215     ## Step 13
6216     undef $i;
6217 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
6218     if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
6219 wakaba 1.79
6220 wakaba 1.3 splice @{$self->{open_elements}}, $_, 1;
6221 wakaba 1.1 $i-- and last OE if defined $i;
6222 wakaba 1.3 } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
6223 wakaba 1.79
6224 wakaba 1.1 $i = $_;
6225     }
6226     } # OE
6227 wakaba 1.3 splice @{$self->{open_elements}}, $i + 1, 1, $clone;
6228 wakaba 1.1
6229     ## Step 14
6230     redo FET;
6231     } # FET
6232     }; # $formatting_end_tag
6233    
6234 wakaba 1.96 $insert = my $insert_to_current = sub {
6235 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
6236 wakaba 1.1 }; # $insert_to_current
6237    
6238     my $insert_to_foster = sub {
6239 wakaba 1.95 my $child = shift;
6240 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
6241 wakaba 1.95 # MUST
6242     my $foster_parent_element;
6243     my $next_sibling;
6244 wakaba 1.121 OE: for (reverse 0..$#{$self->{open_elements}}) {
6245     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
6246 wakaba 1.3 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
6247 wakaba 1.1 if (defined $parent and $parent->node_type == 1) {
6248 wakaba 1.79
6249 wakaba 1.1 $foster_parent_element = $parent;
6250 wakaba 1.3 $next_sibling = $self->{open_elements}->[$_]->[0];
6251 wakaba 1.1 } else {
6252 wakaba 1.79
6253 wakaba 1.1 $foster_parent_element
6254 wakaba 1.3 = $self->{open_elements}->[$_ - 1]->[0];
6255 wakaba 1.1 }
6256     last OE;
6257     }
6258     } # OE
6259 wakaba 1.3 $foster_parent_element = $self->{open_elements}->[0]->[0]
6260 wakaba 1.1 unless defined $foster_parent_element;
6261     $foster_parent_element->insert_before
6262     ($child, $next_sibling);
6263 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
6264     } else {
6265    
6266     $self->{open_elements}->[-1]->[0]->append_child ($child);
6267     }
6268 wakaba 1.1 }; # $insert_to_foster
6269    
6270 wakaba 1.123 B: while (1) {
6271 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
6272 wakaba 1.79
6273 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in html:#DOCTYPE', token => $token);
6274 wakaba 1.54 ## Ignore the token
6275     ## Stay in the phase
6276     $token = $self->_get_next_token;
6277 wakaba 1.123 next B;
6278 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN and
6279 wakaba 1.54 $token->{tag_name} eq 'html') {
6280 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6281 wakaba 1.79
6282 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html', text => 'html', token => $token);
6283 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
6284     } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
6285 wakaba 1.79
6286 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html', text => 'html', token => $token);
6287 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
6288 wakaba 1.79 } else {
6289    
6290 wakaba 1.54 }
6291    
6292 wakaba 1.84
6293 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not first start tag', token => $token);
6294 wakaba 1.54 my $top_el = $self->{open_elements}->[0]->[0];
6295     for my $attr_name (keys %{$token->{attributes}}) {
6296     unless ($top_el->has_attribute_ns (undef, $attr_name)) {
6297 wakaba 1.79
6298 wakaba 1.54 $top_el->set_attribute_ns
6299     (undef, [undef, $attr_name],
6300     $token->{attributes}->{$attr_name}->{value});
6301     }
6302     }
6303 wakaba 1.122
6304 wakaba 1.54 $token = $self->_get_next_token;
6305 wakaba 1.123 next B;
6306 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
6307 wakaba 1.54 my $comment = $self->{document}->create_comment ($token->{data});
6308 wakaba 1.58 if ($self->{insertion_mode} & AFTER_HTML_IMS) {
6309 wakaba 1.79
6310 wakaba 1.54 $self->{document}->append_child ($comment);
6311 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
6312 wakaba 1.79
6313 wakaba 1.54 $self->{open_elements}->[0]->[0]->append_child ($comment);
6314     } else {
6315 wakaba 1.79
6316 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($comment);
6317     }
6318     $token = $self->_get_next_token;
6319 wakaba 1.123 next B;
6320     } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
6321     if ($token->{type} == CHARACTER_TOKEN) {
6322    
6323     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6324     $token = $self->_get_next_token;
6325     next B;
6326     } elsif ($token->{type} == START_TAG_TOKEN) {
6327 wakaba 1.126 if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
6328     $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
6329 wakaba 1.123 not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
6330     ($token->{tag_name} eq 'svg' and
6331     $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
6332     ## NOTE: "using the rules for secondary insertion mode"then"continue"
6333    
6334     #
6335     } elsif ({
6336 wakaba 1.127 b => 1, big => 1, blockquote => 1, body => 1, br => 1,
6337 wakaba 1.143 center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
6338     em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
6339     h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
6340     img => 1, li => 1, listing => 1, menu => 1, meta => 1,
6341     nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
6342     small => 1, span => 1, strong => 1, strike => 1, sub => 1,
6343     sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
6344 wakaba 1.123 }->{$token->{tag_name}}) {
6345    
6346 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
6347     text => $self->{open_elements}->[-1]->[0]
6348 wakaba 1.123 ->manakai_local_name,
6349     token => $token);
6350    
6351     pop @{$self->{open_elements}}
6352     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
6353    
6354 wakaba 1.127 $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
6355 wakaba 1.123 ## Reprocess.
6356     next B;
6357     } else {
6358 wakaba 1.128 my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
6359     my $tag_name = $token->{tag_name};
6360     if ($nsuri eq $SVG_NS) {
6361     $tag_name = {
6362     altglyph => 'altGlyph',
6363     altglyphdef => 'altGlyphDef',
6364     altglyphitem => 'altGlyphItem',
6365     animatecolor => 'animateColor',
6366     animatemotion => 'animateMotion',
6367     animatetransform => 'animateTransform',
6368     clippath => 'clipPath',
6369     feblend => 'feBlend',
6370     fecolormatrix => 'feColorMatrix',
6371     fecomponenttransfer => 'feComponentTransfer',
6372     fecomposite => 'feComposite',
6373     feconvolvematrix => 'feConvolveMatrix',
6374     fediffuselighting => 'feDiffuseLighting',
6375     fedisplacementmap => 'feDisplacementMap',
6376     fedistantlight => 'feDistantLight',
6377     feflood => 'feFlood',
6378     fefunca => 'feFuncA',
6379     fefuncb => 'feFuncB',
6380     fefuncg => 'feFuncG',
6381     fefuncr => 'feFuncR',
6382     fegaussianblur => 'feGaussianBlur',
6383     feimage => 'feImage',
6384     femerge => 'feMerge',
6385     femergenode => 'feMergeNode',
6386     femorphology => 'feMorphology',
6387     feoffset => 'feOffset',
6388     fepointlight => 'fePointLight',
6389     fespecularlighting => 'feSpecularLighting',
6390     fespotlight => 'feSpotLight',
6391     fetile => 'feTile',
6392     feturbulence => 'feTurbulence',
6393     foreignobject => 'foreignObject',
6394     glyphref => 'glyphRef',
6395     lineargradient => 'linearGradient',
6396     radialgradient => 'radialGradient',
6397     #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
6398     textpath => 'textPath',
6399     }->{$tag_name} || $tag_name;
6400     }
6401    
6402     ## "adjust SVG attributes" (SVG only) - done in insert-element-f
6403    
6404     ## "adjust foreign attributes" - done in insert-element-f
6405 wakaba 1.123
6406    
6407     {
6408     my $el;
6409    
6410     $el = $self->{document}->create_element_ns
6411 wakaba 1.128 ($nsuri, [undef, $tag_name]);
6412 wakaba 1.123
6413     for my $attr_name (keys %{ $token->{attributes}}) {
6414     my $attr_t = $token->{attributes}->{$attr_name};
6415 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (
6416     @{
6417     $foreign_attr_xname->{$attr_name} ||
6418     [undef, [undef,
6419 wakaba 1.152 ($nsuri) eq $SVG_NS ?
6420 wakaba 1.128 ($svg_attr_name->{$attr_name} || $attr_name) :
6421 wakaba 1.152 ($nsuri) eq $MML_NS ?
6422     ($attr_name eq 'definitionurl' ?
6423     'definitionURL' : $attr_name) :
6424 wakaba 1.128 $attr_name]]
6425     }
6426     );
6427 wakaba 1.123 $attr->value ($attr_t->{value});
6428     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6429     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6430     $el->set_attribute_node_ns ($attr);
6431     }
6432    
6433     $el->set_user_data (manakai_source_line => $token->{line})
6434     if defined $token->{line};
6435     $el->set_user_data (manakai_source_column => $token->{column})
6436     if defined $token->{column};
6437    
6438     $insert->($el);
6439 wakaba 1.128 push @{$self->{open_elements}}, [$el, ($el_category_f->{$nsuri}->{ $tag_name} || 0) | FOREIGN_EL];
6440    
6441     if ( $token->{attributes}->{xmlns} and $token->{attributes}->{xmlns}->{value} ne ($nsuri)) {
6442 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad namespace', token => $token);
6443 wakaba 1.128 ## TODO: Error type documentation
6444     }
6445 wakaba 1.123 }
6446    
6447    
6448     if ($self->{self_closing}) {
6449     pop @{$self->{open_elements}};
6450     delete $self->{self_closing};
6451     } else {
6452    
6453     }
6454    
6455     $token = $self->_get_next_token;
6456     next B;
6457     }
6458     } elsif ($token->{type} == END_TAG_TOKEN) {
6459     ## NOTE: "using the rules for secondary insertion mode" then "continue"
6460    
6461     #
6462     } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6463    
6464 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
6465     text => $self->{open_elements}->[-1]->[0]
6466 wakaba 1.143 ->manakai_local_name,
6467     token => $token);
6468    
6469     pop @{$self->{open_elements}}
6470     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
6471    
6472     $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
6473     ## Reprocess.
6474     next B;
6475 wakaba 1.123 } else {
6476     die "$0: $token->{type}: Unknown token type";
6477     }
6478     }
6479    
6480     if ($self->{insertion_mode} & HEAD_IMS) {
6481 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
6482 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
6483 wakaba 1.99 unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6484    
6485     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6486 wakaba 1.174 #
6487 wakaba 1.99 } else {
6488    
6489     ## Ignore the token.
6490 wakaba 1.174 #
6491 wakaba 1.99 }
6492 wakaba 1.54 unless (length $token->{data}) {
6493 wakaba 1.79
6494 wakaba 1.54 $token = $self->_get_next_token;
6495 wakaba 1.123 next B;
6496 wakaba 1.54 }
6497 wakaba 1.173 ## TODO: set $token->{column} appropriately
6498 wakaba 1.54 }
6499    
6500 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6501 wakaba 1.79
6502 wakaba 1.54 ## As if <head>
6503    
6504     $self->{head_element} = $self->{document}->create_element_ns
6505 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6506 wakaba 1.54
6507 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6508     if defined $token->{line};
6509     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6510     if defined $token->{column};
6511    
6512 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6513 wakaba 1.121 push @{$self->{open_elements}},
6514     [$self->{head_element}, $el_category->{head}];
6515 wakaba 1.54
6516     ## Reprocess in the "in head" insertion mode...
6517     pop @{$self->{open_elements}};
6518    
6519     ## Reprocess in the "after head" insertion mode...
6520 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6521 wakaba 1.79
6522 wakaba 1.54 ## As if </noscript>
6523     pop @{$self->{open_elements}};
6524 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:#text', token => $token);
6525 wakaba 1.54
6526     ## Reprocess in the "in head" insertion mode...
6527     ## As if </head>
6528     pop @{$self->{open_elements}};
6529    
6530     ## Reprocess in the "after head" insertion mode...
6531 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6532 wakaba 1.79
6533 wakaba 1.54 pop @{$self->{open_elements}};
6534    
6535     ## Reprocess in the "after head" insertion mode...
6536 wakaba 1.79 } else {
6537    
6538 wakaba 1.54 }
6539    
6540 wakaba 1.121 ## "after head" insertion mode
6541     ## As if <body>
6542    
6543 wakaba 1.54 {
6544     my $el;
6545    
6546     $el = $self->{document}->create_element_ns
6547 wakaba 1.128 ($HTML_NS, [undef, 'body']);
6548 wakaba 1.54
6549 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6550     if defined $token->{line};
6551     $el->set_user_data (manakai_source_column => $token->{column})
6552     if defined $token->{column};
6553    
6554 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6555 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
6556 wakaba 1.54 }
6557    
6558 wakaba 1.121 $self->{insertion_mode} = IN_BODY_IM;
6559     ## reprocess
6560 wakaba 1.123 next B;
6561 wakaba 1.121 } elsif ($token->{type} == START_TAG_TOKEN) {
6562     if ($token->{tag_name} eq 'head') {
6563     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6564    
6565    
6566 wakaba 1.54 $self->{head_element} = $self->{document}->create_element_ns
6567 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6568 wakaba 1.54
6569     for my $attr_name (keys %{ $token->{attributes}}) {
6570 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6571 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6572 wakaba 1.117 $attr->value ($attr_t->{value});
6573     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6574     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6575     $self->{head_element}->set_attribute_node_ns ($attr);
6576 wakaba 1.54 }
6577    
6578 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6579     if defined $token->{line};
6580     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6581     if defined $token->{column};
6582    
6583 wakaba 1.121 $self->{open_elements}->[-1]->[0]->append_child
6584     ($self->{head_element});
6585     push @{$self->{open_elements}},
6586     [$self->{head_element}, $el_category->{head}];
6587     $self->{insertion_mode} = IN_HEAD_IM;
6588 wakaba 1.122
6589     $token = $self->_get_next_token;
6590 wakaba 1.123 next B;
6591 wakaba 1.122 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6592    
6593 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head', text => 'head',
6594     token => $token);
6595 wakaba 1.136 ## Ignore the token
6596    
6597     $token = $self->_get_next_token;
6598     next B;
6599 wakaba 1.122 } else {
6600    
6601 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in head:head',
6602     token => $token); # or in head noscript
6603 wakaba 1.122 ## Ignore the token
6604    
6605 wakaba 1.121 $token = $self->_get_next_token;
6606 wakaba 1.123 next B;
6607 wakaba 1.122 }
6608     } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6609 wakaba 1.123
6610     ## As if <head>
6611    
6612 wakaba 1.54 $self->{head_element} = $self->{document}->create_element_ns
6613 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6614 wakaba 1.54
6615 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6616     if defined $token->{line};
6617     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6618     if defined $token->{column};
6619    
6620 wakaba 1.123 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6621     push @{$self->{open_elements}},
6622     [$self->{head_element}, $el_category->{head}];
6623 wakaba 1.54
6624 wakaba 1.123 $self->{insertion_mode} = IN_HEAD_IM;
6625     ## Reprocess in the "in head" insertion mode...
6626     } else {
6627    
6628     }
6629 wakaba 1.54
6630     if ($token->{tag_name} eq 'base') {
6631 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6632 wakaba 1.79
6633 wakaba 1.54 ## As if </noscript>
6634     pop @{$self->{open_elements}};
6635 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'base',
6636     token => $token);
6637 wakaba 1.54
6638 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
6639 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
6640 wakaba 1.79 } else {
6641    
6642 wakaba 1.54 }
6643    
6644     ## NOTE: There is a "as if in head" code clone.
6645 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6646 wakaba 1.79
6647 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6648     text => $token->{tag_name}, token => $token);
6649 wakaba 1.121 push @{$self->{open_elements}},
6650     [$self->{head_element}, $el_category->{head}];
6651 wakaba 1.79 } else {
6652    
6653 wakaba 1.54 }
6654    
6655     {
6656     my $el;
6657    
6658     $el = $self->{document}->create_element_ns
6659 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6660 wakaba 1.54
6661     for my $attr_name (keys %{ $token->{attributes}}) {
6662 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6663 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6664 wakaba 1.117 $attr->value ($attr_t->{value});
6665     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6666     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6667     $el->set_attribute_node_ns ($attr);
6668 wakaba 1.54 }
6669    
6670 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6671     if defined $token->{line};
6672     $el->set_user_data (manakai_source_column => $token->{column})
6673     if defined $token->{column};
6674    
6675 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6676 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6677 wakaba 1.54 }
6678    
6679     pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6680 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6681 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6682 wakaba 1.122
6683 wakaba 1.54 $token = $self->_get_next_token;
6684 wakaba 1.123 next B;
6685 wakaba 1.54 } elsif ($token->{tag_name} eq 'link') {
6686     ## NOTE: There is a "as if in head" code clone.
6687 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6688 wakaba 1.79
6689 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6690     text => $token->{tag_name}, token => $token);
6691 wakaba 1.121 push @{$self->{open_elements}},
6692     [$self->{head_element}, $el_category->{head}];
6693 wakaba 1.79 } else {
6694    
6695 wakaba 1.54 }
6696    
6697 wakaba 1.25 {
6698     my $el;
6699    
6700 wakaba 1.1 $el = $self->{document}->create_element_ns
6701 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6702 wakaba 1.1
6703 wakaba 1.25 for my $attr_name (keys %{ $token->{attributes}}) {
6704 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6705 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6706 wakaba 1.117 $attr->value ($attr_t->{value});
6707     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6708     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6709     $el->set_attribute_node_ns ($attr);
6710 wakaba 1.1 }
6711    
6712 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6713     if defined $token->{line};
6714     $el->set_user_data (manakai_source_column => $token->{column})
6715     if defined $token->{column};
6716    
6717 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6718 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6719 wakaba 1.25 }
6720    
6721 wakaba 1.54 pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6722 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6723 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6724 wakaba 1.122 delete $self->{self_closing};
6725 wakaba 1.54 $token = $self->_get_next_token;
6726 wakaba 1.123 next B;
6727 wakaba 1.54 } elsif ($token->{tag_name} eq 'meta') {
6728     ## NOTE: There is a "as if in head" code clone.
6729 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6730 wakaba 1.79
6731 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6732     text => $token->{tag_name}, token => $token);
6733 wakaba 1.121 push @{$self->{open_elements}},
6734     [$self->{head_element}, $el_category->{head}];
6735 wakaba 1.79 } else {
6736    
6737 wakaba 1.54 }
6738    
6739 wakaba 1.34 {
6740     my $el;
6741    
6742     $el = $self->{document}->create_element_ns
6743 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6744 wakaba 1.34
6745     for my $attr_name (keys %{ $token->{attributes}}) {
6746 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6747 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6748 wakaba 1.117 $attr->value ($attr_t->{value});
6749     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6750     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6751     $el->set_attribute_node_ns ($attr);
6752 wakaba 1.34 }
6753    
6754 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6755     if defined $token->{line};
6756     $el->set_user_data (manakai_source_column => $token->{column})
6757     if defined $token->{column};
6758    
6759 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6760 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6761 wakaba 1.34 }
6762    
6763 wakaba 1.68 my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6764 wakaba 1.34
6765 wakaba 1.54 unless ($self->{confident}) {
6766 wakaba 1.131 if ($token->{attributes}->{charset}) {
6767 wakaba 1.79
6768 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
6769     ## in the {change_encoding} callback.
6770 wakaba 1.65 $self->{change_encoding}
6771 wakaba 1.112 ->($self, $token->{attributes}->{charset}->{value},
6772     $token);
6773 wakaba 1.68
6774     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6775     ->set_user_data (manakai_has_reference =>
6776     $token->{attributes}->{charset}
6777     ->{has_reference});
6778 wakaba 1.65 } elsif ($token->{attributes}->{content}) {
6779     if ($token->{attributes}->{content}->{value}
6780 wakaba 1.141 =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6781 wakaba 1.71 [\x09-\x0D\x20]*=
6782 wakaba 1.54 [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6783 wakaba 1.142 ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {
6784 wakaba 1.79
6785 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
6786     ## in the {change_encoding} callback.
6787 wakaba 1.65 $self->{change_encoding}
6788 wakaba 1.112 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
6789     $token);
6790 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6791     ->set_user_data (manakai_has_reference =>
6792     $token->{attributes}->{content}
6793     ->{has_reference});
6794 wakaba 1.79 } else {
6795    
6796 wakaba 1.65 }
6797 wakaba 1.54 }
6798 wakaba 1.68 } else {
6799     if ($token->{attributes}->{charset}) {
6800 wakaba 1.79
6801 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6802     ->set_user_data (manakai_has_reference =>
6803     $token->{attributes}->{charset}
6804     ->{has_reference});
6805     }
6806 wakaba 1.69 if ($token->{attributes}->{content}) {
6807 wakaba 1.79
6808 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6809     ->set_user_data (manakai_has_reference =>
6810     $token->{attributes}->{content}
6811     ->{has_reference});
6812     }
6813 wakaba 1.54 }
6814 wakaba 1.34
6815 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6816 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6817 wakaba 1.122 delete $self->{self_closing};
6818 wakaba 1.54 $token = $self->_get_next_token;
6819 wakaba 1.123 next B;
6820 wakaba 1.54 } elsif ($token->{tag_name} eq 'title') {
6821 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6822 wakaba 1.79
6823 wakaba 1.54 ## As if </noscript>
6824     pop @{$self->{open_elements}};
6825 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'title',
6826     token => $token);
6827 wakaba 1.1
6828 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
6829 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
6830 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6831 wakaba 1.79
6832 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6833     text => $token->{tag_name}, token => $token);
6834 wakaba 1.121 push @{$self->{open_elements}},
6835     [$self->{head_element}, $el_category->{head}];
6836 wakaba 1.79 } else {
6837    
6838 wakaba 1.54 }
6839    
6840     ## NOTE: There is a "as if in head" code clone.
6841     my $parent = defined $self->{head_element} ? $self->{head_element}
6842     : $self->{open_elements}->[-1]->[0];
6843 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
6844 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6845 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6846 wakaba 1.123 next B;
6847 wakaba 1.145 } elsif ($token->{tag_name} eq 'style' or
6848     $token->{tag_name} eq 'noframes') {
6849 wakaba 1.54 ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
6850 wakaba 1.56 ## insertion mode IN_HEAD_IM)
6851 wakaba 1.54 ## NOTE: There is a "as if in head" code clone.
6852 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6853 wakaba 1.79
6854 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6855     text => $token->{tag_name}, token => $token);
6856 wakaba 1.121 push @{$self->{open_elements}},
6857     [$self->{head_element}, $el_category->{head}];
6858 wakaba 1.79 } else {
6859    
6860 wakaba 1.54 }
6861 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
6862 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6863 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6864 wakaba 1.123 next B;
6865 wakaba 1.54 } elsif ($token->{tag_name} eq 'noscript') {
6866 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_IM) {
6867 wakaba 1.79
6868 wakaba 1.54 ## NOTE: and scripting is disalbed
6869    
6870 wakaba 1.1 {
6871     my $el;
6872    
6873     $el = $self->{document}->create_element_ns
6874 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6875 wakaba 1.1
6876     for my $attr_name (keys %{ $token->{attributes}}) {
6877 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6878 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6879 wakaba 1.117 $attr->value ($attr_t->{value});
6880     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6881     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6882     $el->set_attribute_node_ns ($attr);
6883 wakaba 1.1 }
6884    
6885 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6886     if defined $token->{line};
6887     $el->set_user_data (manakai_source_column => $token->{column})
6888     if defined $token->{column};
6889    
6890 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6891 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6892 wakaba 1.1 }
6893    
6894 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
6895 wakaba 1.122
6896 wakaba 1.54 $token = $self->_get_next_token;
6897 wakaba 1.123 next B;
6898 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6899 wakaba 1.79
6900 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'noscript',
6901     token => $token);
6902 wakaba 1.54 ## Ignore the token
6903 wakaba 1.122
6904 wakaba 1.54 $token = $self->_get_next_token;
6905 wakaba 1.123 next B;
6906 wakaba 1.54 } else {
6907 wakaba 1.79
6908 wakaba 1.54 #
6909     }
6910     } elsif ($token->{tag_name} eq 'script') {
6911 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6912 wakaba 1.79
6913 wakaba 1.54 ## As if </noscript>
6914     pop @{$self->{open_elements}};
6915 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'script',
6916     token => $token);
6917 wakaba 1.54
6918 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
6919 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
6920 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6921 wakaba 1.79
6922 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6923     text => $token->{tag_name}, token => $token);
6924 wakaba 1.121 push @{$self->{open_elements}},
6925     [$self->{head_element}, $el_category->{head}];
6926 wakaba 1.79 } else {
6927    
6928 wakaba 1.54 }
6929    
6930     ## NOTE: There is a "as if in head" code clone.
6931 wakaba 1.100 $script_start_tag->();
6932     pop @{$self->{open_elements}} # <head>
6933 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6934 wakaba 1.123 next B;
6935 wakaba 1.54 } elsif ($token->{tag_name} eq 'body' or
6936     $token->{tag_name} eq 'frameset') {
6937 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6938 wakaba 1.79
6939 wakaba 1.54 ## As if </noscript>
6940     pop @{$self->{open_elements}};
6941 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript',
6942     text => $token->{tag_name}, token => $token);
6943 wakaba 1.54
6944     ## Reprocess in the "in head" insertion mode...
6945     ## As if </head>
6946     pop @{$self->{open_elements}};
6947    
6948     ## Reprocess in the "after head" insertion mode...
6949 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6950 wakaba 1.79
6951 wakaba 1.54 pop @{$self->{open_elements}};
6952    
6953     ## Reprocess in the "after head" insertion mode...
6954 wakaba 1.79 } else {
6955    
6956 wakaba 1.54 }
6957    
6958     ## "after head" insertion mode
6959    
6960 wakaba 1.1 {
6961     my $el;
6962    
6963     $el = $self->{document}->create_element_ns
6964 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6965 wakaba 1.1
6966     for my $attr_name (keys %{ $token->{attributes}}) {
6967 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6968 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6969 wakaba 1.117 $attr->value ($attr_t->{value});
6970     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6971     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6972     $el->set_attribute_node_ns ($attr);
6973 wakaba 1.1 }
6974    
6975 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6976     if defined $token->{line};
6977     $el->set_user_data (manakai_source_column => $token->{column})
6978     if defined $token->{column};
6979    
6980 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6981 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6982 wakaba 1.1 }
6983    
6984 wakaba 1.56 if ($token->{tag_name} eq 'body') {
6985 wakaba 1.79
6986 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
6987     } elsif ($token->{tag_name} eq 'frameset') {
6988 wakaba 1.79
6989 wakaba 1.56 $self->{insertion_mode} = IN_FRAMESET_IM;
6990     } else {
6991     die "$0: tag name: $self->{tag_name}";
6992     }
6993 wakaba 1.122
6994 wakaba 1.54 $token = $self->_get_next_token;
6995 wakaba 1.123 next B;
6996 wakaba 1.54 } else {
6997 wakaba 1.79
6998 wakaba 1.54 #
6999 wakaba 1.8 }
7000 wakaba 1.54
7001 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
7002 wakaba 1.79
7003 wakaba 1.54 ## As if </noscript>
7004     pop @{$self->{open_elements}};
7005 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
7006     text => $token->{tag_name}, token => $token);
7007 wakaba 1.54
7008     ## Reprocess in the "in head" insertion mode...
7009     ## As if </head>
7010     pop @{$self->{open_elements}};
7011    
7012     ## Reprocess in the "after head" insertion mode...
7013 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
7014 wakaba 1.79
7015 wakaba 1.54 ## As if </head>
7016     pop @{$self->{open_elements}};
7017    
7018     ## Reprocess in the "after head" insertion mode...
7019 wakaba 1.79 } else {
7020    
7021 wakaba 1.54 }
7022    
7023     ## "after head" insertion mode
7024     ## As if <body>
7025    
7026 wakaba 1.1 {
7027     my $el;
7028    
7029     $el = $self->{document}->create_element_ns
7030 wakaba 1.128 ($HTML_NS, [undef, 'body']);
7031 wakaba 1.1
7032 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7033     if defined $token->{line};
7034     $el->set_user_data (manakai_source_column => $token->{column})
7035     if defined $token->{column};
7036    
7037 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7038 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
7039 wakaba 1.1 }
7040    
7041 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
7042 wakaba 1.54 ## reprocess
7043 wakaba 1.122
7044 wakaba 1.123 next B;
7045 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
7046 wakaba 1.54 if ($token->{tag_name} eq 'head') {
7047 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
7048 wakaba 1.79
7049 wakaba 1.54 ## As if <head>
7050    
7051     $self->{head_element} = $self->{document}->create_element_ns
7052 wakaba 1.128 ($HTML_NS, [undef, 'head']);
7053 wakaba 1.54
7054 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
7055     if defined $token->{line};
7056     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
7057     if defined $token->{column};
7058    
7059 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
7060 wakaba 1.121 push @{$self->{open_elements}},
7061     [$self->{head_element}, $el_category->{head}];
7062 wakaba 1.54
7063     ## Reprocess in the "in head" insertion mode...
7064     pop @{$self->{open_elements}};
7065 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
7066 wakaba 1.54 $token = $self->_get_next_token;
7067 wakaba 1.123 next B;
7068 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
7069 wakaba 1.79
7070 wakaba 1.54 ## As if </noscript>
7071     pop @{$self->{open_elements}};
7072 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
7073     text => 'head', token => $token);
7074 wakaba 1.54
7075     ## Reprocess in the "in head" insertion mode...
7076     pop @{$self->{open_elements}};
7077 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
7078 wakaba 1.54 $token = $self->_get_next_token;
7079 wakaba 1.123 next B;
7080 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
7081 wakaba 1.79
7082 wakaba 1.54 pop @{$self->{open_elements}};
7083 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
7084 wakaba 1.54 $token = $self->_get_next_token;
7085 wakaba 1.123 next B;
7086 wakaba 1.136 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
7087    
7088 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => 'head',
7089     token => $token);
7090 wakaba 1.136 ## Ignore the token
7091     $token = $self->_get_next_token;
7092     next B;
7093 wakaba 1.54 } else {
7094 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
7095 wakaba 1.54 }
7096     } elsif ($token->{tag_name} eq 'noscript') {
7097 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
7098 wakaba 1.79
7099 wakaba 1.54 pop @{$self->{open_elements}};
7100 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
7101 wakaba 1.54 $token = $self->_get_next_token;
7102 wakaba 1.123 next B;
7103 wakaba 1.136 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
7104     $self->{insertion_mode} == AFTER_HEAD_IM) {
7105 wakaba 1.79
7106 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7107     text => 'noscript', token => $token);
7108 wakaba 1.54 ## Ignore the token ## ISSUE: An issue in the spec.
7109     $token = $self->_get_next_token;
7110 wakaba 1.123 next B;
7111 wakaba 1.54 } else {
7112 wakaba 1.79
7113 wakaba 1.54 #
7114     }
7115     } elsif ({
7116     body => 1, html => 1,
7117     }->{$token->{tag_name}}) {
7118 wakaba 1.136 if ($self->{insertion_mode} == BEFORE_HEAD_IM or
7119     $self->{insertion_mode} == IN_HEAD_IM or
7120     $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
7121 wakaba 1.79
7122 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7123     text => $token->{tag_name}, token => $token);
7124 wakaba 1.136 ## Ignore the token
7125     $token = $self->_get_next_token;
7126     next B;
7127     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
7128 wakaba 1.79
7129 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7130     text => $token->{tag_name}, token => $token);
7131 wakaba 1.54 ## Ignore the token
7132     $token = $self->_get_next_token;
7133 wakaba 1.123 next B;
7134 wakaba 1.79 } else {
7135 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
7136 wakaba 1.54 }
7137 wakaba 1.136 } elsif ($token->{tag_name} eq 'p') {
7138    
7139 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7140     text => $token->{tag_name}, token => $token);
7141 wakaba 1.136 ## Ignore the token
7142     $token = $self->_get_next_token;
7143     next B;
7144     } elsif ($token->{tag_name} eq 'br') {
7145 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
7146 wakaba 1.79
7147 wakaba 1.136 ## (before head) as if <head>, (in head) as if </head>
7148 wakaba 1.54
7149     $self->{head_element} = $self->{document}->create_element_ns
7150 wakaba 1.128 ($HTML_NS, [undef, 'head']);
7151 wakaba 1.54
7152 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
7153     if defined $token->{line};
7154     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
7155     if defined $token->{column};
7156    
7157 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
7158 wakaba 1.136 $self->{insertion_mode} = AFTER_HEAD_IM;
7159    
7160     ## Reprocess in the "after head" insertion mode...
7161     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
7162    
7163     ## As if </head>
7164     pop @{$self->{open_elements}};
7165     $self->{insertion_mode} = AFTER_HEAD_IM;
7166    
7167     ## Reprocess in the "after head" insertion mode...
7168     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
7169    
7170     ## ISSUE: Two parse errors for <head><noscript></br>
7171 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7172     text => 'br', token => $token);
7173 wakaba 1.136 ## As if </noscript>
7174     pop @{$self->{open_elements}};
7175     $self->{insertion_mode} = IN_HEAD_IM;
7176 wakaba 1.54
7177     ## Reprocess in the "in head" insertion mode...
7178 wakaba 1.136 ## As if </head>
7179     pop @{$self->{open_elements}};
7180     $self->{insertion_mode} = AFTER_HEAD_IM;
7181 wakaba 1.54
7182 wakaba 1.136 ## Reprocess in the "after head" insertion mode...
7183     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
7184 wakaba 1.79
7185 wakaba 1.56 #
7186     } else {
7187 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
7188 wakaba 1.54 }
7189 wakaba 1.136
7190     ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
7191 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7192     text => 'br', token => $token);
7193 wakaba 1.136 ## Ignore the token
7194     $token = $self->_get_next_token;
7195     next B;
7196     } else {
7197    
7198 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7199     text => $token->{tag_name}, token => $token);
7200 wakaba 1.136 ## Ignore the token
7201     $token = $self->_get_next_token;
7202     next B;
7203 wakaba 1.54 }
7204    
7205 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
7206 wakaba 1.79
7207 wakaba 1.54 ## As if </noscript>
7208     pop @{$self->{open_elements}};
7209 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
7210     text => $token->{tag_name}, token => $token);
7211 wakaba 1.54
7212     ## Reprocess in the "in head" insertion mode...
7213     ## As if </head>
7214     pop @{$self->{open_elements}};
7215    
7216     ## Reprocess in the "after head" insertion mode...
7217 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
7218 wakaba 1.79
7219 wakaba 1.54 ## As if </head>
7220     pop @{$self->{open_elements}};
7221    
7222     ## Reprocess in the "after head" insertion mode...
7223 wakaba 1.56 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
7224 wakaba 1.82 ## ISSUE: This case cannot be reached?
7225 wakaba 1.79
7226 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7227     text => $token->{tag_name}, token => $token);
7228 wakaba 1.54 ## Ignore the token ## ISSUE: An issue in the spec.
7229     $token = $self->_get_next_token;
7230 wakaba 1.123 next B;
7231 wakaba 1.79 } else {
7232    
7233 wakaba 1.8 }
7234 wakaba 1.54
7235     ## "after head" insertion mode
7236     ## As if <body>
7237    
7238 wakaba 1.1 {
7239     my $el;
7240    
7241     $el = $self->{document}->create_element_ns
7242 wakaba 1.128 ($HTML_NS, [undef, 'body']);
7243 wakaba 1.1
7244 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7245     if defined $token->{line};
7246     $el->set_user_data (manakai_source_column => $token->{column})
7247     if defined $token->{column};
7248    
7249 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7250 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
7251 wakaba 1.1 }
7252    
7253 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
7254 wakaba 1.54 ## reprocess
7255 wakaba 1.123 next B;
7256 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7257     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
7258    
7259    
7260     ## NOTE: As if <head>
7261    
7262     $self->{head_element} = $self->{document}->create_element_ns
7263 wakaba 1.128 ($HTML_NS, [undef, 'head']);
7264 wakaba 1.103
7265 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
7266     if defined $token->{line};
7267     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
7268     if defined $token->{column};
7269    
7270 wakaba 1.103 $self->{open_elements}->[-1]->[0]->append_child
7271     ($self->{head_element});
7272 wakaba 1.121 #push @{$self->{open_elements}},
7273     # [$self->{head_element}, $el_category->{head}];
7274 wakaba 1.103 #$self->{insertion_mode} = IN_HEAD_IM;
7275     ## NOTE: Reprocess.
7276    
7277     ## NOTE: As if </head>
7278     #pop @{$self->{open_elements}};
7279     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
7280     ## NOTE: Reprocess.
7281    
7282     #
7283     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
7284    
7285    
7286     ## NOTE: As if </head>
7287     pop @{$self->{open_elements}};
7288     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
7289     ## NOTE: Reprocess.
7290    
7291     #
7292     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
7293    
7294    
7295 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:#eof', token => $token);
7296 wakaba 1.103
7297     ## As if </noscript>
7298     pop @{$self->{open_elements}};
7299     #$self->{insertion_mode} = IN_HEAD_IM;
7300     ## NOTE: Reprocess.
7301    
7302     ## NOTE: As if </head>
7303     pop @{$self->{open_elements}};
7304     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
7305     ## NOTE: Reprocess.
7306    
7307     #
7308     } else {
7309    
7310     #
7311     }
7312    
7313     ## NOTE: As if <body>
7314    
7315     {
7316     my $el;
7317    
7318     $el = $self->{document}->create_element_ns
7319 wakaba 1.128 ($HTML_NS, [undef, 'body']);
7320 wakaba 1.103
7321 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7322     if defined $token->{line};
7323     $el->set_user_data (manakai_source_column => $token->{column})
7324     if defined $token->{column};
7325    
7326 wakaba 1.103 $self->{open_elements}->[-1]->[0]->append_child ($el);
7327 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
7328 wakaba 1.103 }
7329    
7330     $self->{insertion_mode} = IN_BODY_IM;
7331     ## NOTE: Reprocess.
7332 wakaba 1.123 next B;
7333 wakaba 1.103 } else {
7334     die "$0: $token->{type}: Unknown token type";
7335     }
7336 wakaba 1.54
7337     ## ISSUE: An issue in the spec.
7338 wakaba 1.58 } elsif ($self->{insertion_mode} & BODY_IMS) {
7339 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
7340 wakaba 1.79
7341 wakaba 1.54 ## NOTE: There is a code clone of "character in body".
7342     $reconstruct_active_formatting_elements->($insert_to_current);
7343    
7344     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
7345    
7346     $token = $self->_get_next_token;
7347 wakaba 1.123 next B;
7348 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
7349 wakaba 1.54 if ({
7350     caption => 1, col => 1, colgroup => 1, tbody => 1,
7351     td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
7352     }->{$token->{tag_name}}) {
7353 wakaba 1.56 if ($self->{insertion_mode} == IN_CELL_IM) {
7354 wakaba 1.54 ## have an element in table scope
7355 wakaba 1.106 for (reverse 0..$#{$self->{open_elements}}) {
7356 wakaba 1.54 my $node = $self->{open_elements}->[$_];
7357 wakaba 1.121 if ($node->[1] & TABLE_CELL_EL) {
7358 wakaba 1.79
7359 wakaba 1.106
7360     ## Close the cell
7361 wakaba 1.122
7362     $token->{self_closing} = $self->{self_closing};
7363     unshift @{$self->{token}}, $token;
7364     delete $self->{self_closing};
7365     # <x>
7366 wakaba 1.120 $token = {type => END_TAG_TOKEN,
7367     tag_name => $node->[0]->manakai_local_name,
7368 wakaba 1.112 line => $token->{line},
7369     column => $token->{column}};
7370 wakaba 1.123 next B;
7371 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7372 wakaba 1.79
7373 wakaba 1.106 ## ISSUE: This case can never be reached, maybe.
7374     last;
7375 wakaba 1.54 }
7376 wakaba 1.106 }
7377    
7378 wakaba 1.54
7379 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'start tag not allowed',
7380     text => $token->{tag_name}, token => $token);
7381 wakaba 1.106 ## Ignore the token
7382 wakaba 1.122
7383 wakaba 1.106 $token = $self->_get_next_token;
7384 wakaba 1.123 next B;
7385 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
7386 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'caption',
7387     token => $token);
7388 wakaba 1.54
7389 wakaba 1.106 ## NOTE: As if </caption>.
7390 wakaba 1.54 ## have a table element in table scope
7391     my $i;
7392 wakaba 1.106 INSCOPE: {
7393     for (reverse 0..$#{$self->{open_elements}}) {
7394     my $node = $self->{open_elements}->[$_];
7395 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
7396 wakaba 1.106
7397     $i = $_;
7398     last INSCOPE;
7399 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7400 wakaba 1.106
7401     last;
7402     }
7403 wakaba 1.54 }
7404 wakaba 1.106
7405    
7406 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'start tag not allowed',
7407     text => $token->{tag_name}, token => $token);
7408 wakaba 1.106 ## Ignore the token
7409 wakaba 1.122
7410 wakaba 1.106 $token = $self->_get_next_token;
7411 wakaba 1.123 next B;
7412 wakaba 1.54 } # INSCOPE
7413    
7414     ## generate implied end tags
7415 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7416     & END_TAG_OPTIONAL_EL) {
7417 wakaba 1.79
7418 wakaba 1.86 pop @{$self->{open_elements}};
7419 wakaba 1.54 }
7420    
7421 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7422 wakaba 1.79
7423 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7424     text => $self->{open_elements}->[-1]->[0]
7425 wakaba 1.120 ->manakai_local_name,
7426     token => $token);
7427 wakaba 1.79 } else {
7428    
7429 wakaba 1.54 }
7430    
7431     splice @{$self->{open_elements}}, $i;
7432    
7433     $clear_up_to_marker->();
7434    
7435 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7436 wakaba 1.54
7437     ## reprocess
7438 wakaba 1.122
7439 wakaba 1.123 next B;
7440 wakaba 1.54 } else {
7441 wakaba 1.79
7442 wakaba 1.54 #
7443     }
7444     } else {
7445 wakaba 1.79
7446 wakaba 1.54 #
7447     }
7448 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
7449 wakaba 1.54 if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
7450 wakaba 1.56 if ($self->{insertion_mode} == IN_CELL_IM) {
7451 wakaba 1.54 ## have an element in table scope
7452     my $i;
7453     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7454     my $node = $self->{open_elements}->[$_];
7455 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7456 wakaba 1.79
7457 wakaba 1.54 $i = $_;
7458     last INSCOPE;
7459 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7460 wakaba 1.79
7461 wakaba 1.54 last INSCOPE;
7462     }
7463     } # INSCOPE
7464     unless (defined $i) {
7465 wakaba 1.79
7466 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7467     text => $token->{tag_name},
7468     token => $token);
7469 wakaba 1.54 ## Ignore the token
7470     $token = $self->_get_next_token;
7471 wakaba 1.123 next B;
7472 wakaba 1.54 }
7473    
7474     ## generate implied end tags
7475 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7476     & END_TAG_OPTIONAL_EL) {
7477 wakaba 1.79
7478 wakaba 1.86 pop @{$self->{open_elements}};
7479 wakaba 1.54 }
7480 wakaba 1.86
7481 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7482     ne $token->{tag_name}) {
7483 wakaba 1.79
7484 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7485     text => $self->{open_elements}->[-1]->[0]
7486 wakaba 1.120 ->manakai_local_name,
7487     token => $token);
7488 wakaba 1.79 } else {
7489    
7490 wakaba 1.54 }
7491    
7492     splice @{$self->{open_elements}}, $i;
7493    
7494     $clear_up_to_marker->();
7495    
7496 wakaba 1.56 $self->{insertion_mode} = IN_ROW_IM;
7497 wakaba 1.54
7498     $token = $self->_get_next_token;
7499 wakaba 1.123 next B;
7500 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
7501 wakaba 1.79
7502 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7503     text => $token->{tag_name}, token => $token);
7504 wakaba 1.54 ## Ignore the token
7505     $token = $self->_get_next_token;
7506 wakaba 1.123 next B;
7507 wakaba 1.54 } else {
7508 wakaba 1.79
7509 wakaba 1.54 #
7510     }
7511     } elsif ($token->{tag_name} eq 'caption') {
7512 wakaba 1.56 if ($self->{insertion_mode} == IN_CAPTION_IM) {
7513 wakaba 1.54 ## have a table element in table scope
7514     my $i;
7515 wakaba 1.106 INSCOPE: {
7516     for (reverse 0..$#{$self->{open_elements}}) {
7517     my $node = $self->{open_elements}->[$_];
7518 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
7519 wakaba 1.106
7520     $i = $_;
7521     last INSCOPE;
7522 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7523 wakaba 1.106
7524     last;
7525     }
7526 wakaba 1.54 }
7527 wakaba 1.106
7528    
7529 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7530     text => $token->{tag_name}, token => $token);
7531 wakaba 1.106 ## Ignore the token
7532     $token = $self->_get_next_token;
7533 wakaba 1.123 next B;
7534 wakaba 1.54 } # INSCOPE
7535    
7536     ## generate implied end tags
7537 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7538     & END_TAG_OPTIONAL_EL) {
7539 wakaba 1.79
7540 wakaba 1.86 pop @{$self->{open_elements}};
7541 wakaba 1.54 }
7542    
7543 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7544 wakaba 1.79
7545 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7546     text => $self->{open_elements}->[-1]->[0]
7547 wakaba 1.120 ->manakai_local_name,
7548     token => $token);
7549 wakaba 1.79 } else {
7550    
7551 wakaba 1.54 }
7552    
7553     splice @{$self->{open_elements}}, $i;
7554    
7555     $clear_up_to_marker->();
7556    
7557 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7558 wakaba 1.54
7559     $token = $self->_get_next_token;
7560 wakaba 1.123 next B;
7561 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CELL_IM) {
7562 wakaba 1.79
7563 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7564     text => $token->{tag_name}, token => $token);
7565 wakaba 1.54 ## Ignore the token
7566     $token = $self->_get_next_token;
7567 wakaba 1.123 next B;
7568 wakaba 1.54 } else {
7569 wakaba 1.79
7570 wakaba 1.54 #
7571 wakaba 1.1 }
7572 wakaba 1.54 } elsif ({
7573     table => 1, tbody => 1, tfoot => 1,
7574     thead => 1, tr => 1,
7575     }->{$token->{tag_name}} and
7576 wakaba 1.56 $self->{insertion_mode} == IN_CELL_IM) {
7577 wakaba 1.54 ## have an element in table scope
7578     my $i;
7579     my $tn;
7580 wakaba 1.106 INSCOPE: {
7581     for (reverse 0..$#{$self->{open_elements}}) {
7582     my $node = $self->{open_elements}->[$_];
7583 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7584 wakaba 1.106
7585     $i = $_;
7586    
7587     ## Close the cell
7588 wakaba 1.122
7589     $token->{self_closing} = $self->{self_closing};
7590     unshift @{$self->{token}}, $token;
7591     delete $self->{self_closing};
7592     # </x>
7593 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => $tn,
7594     line => $token->{line},
7595     column => $token->{column}};
7596 wakaba 1.123 next B;
7597 wakaba 1.121 } elsif ($node->[1] & TABLE_CELL_EL) {
7598 wakaba 1.106
7599 wakaba 1.121 $tn = $node->[0]->manakai_local_name;
7600 wakaba 1.106 ## NOTE: There is exactly one |td| or |th| element
7601     ## in scope in the stack of open elements by definition.
7602 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7603 wakaba 1.106 ## ISSUE: Can this be reached?
7604    
7605     last;
7606     }
7607 wakaba 1.54 }
7608 wakaba 1.106
7609 wakaba 1.79
7610 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7611     text => $token->{tag_name}, token => $token);
7612 wakaba 1.54 ## Ignore the token
7613     $token = $self->_get_next_token;
7614 wakaba 1.123 next B;
7615 wakaba 1.106 } # INSCOPE
7616 wakaba 1.54 } elsif ($token->{tag_name} eq 'table' and
7617 wakaba 1.56 $self->{insertion_mode} == IN_CAPTION_IM) {
7618 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'caption',
7619     token => $token);
7620 wakaba 1.31
7621 wakaba 1.54 ## As if </caption>
7622     ## have a table element in table scope
7623     my $i;
7624     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7625     my $node = $self->{open_elements}->[$_];
7626 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
7627 wakaba 1.79
7628 wakaba 1.54 $i = $_;
7629     last INSCOPE;
7630 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7631 wakaba 1.79
7632 wakaba 1.54 last INSCOPE;
7633     }
7634     } # INSCOPE
7635     unless (defined $i) {
7636 wakaba 1.79
7637 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7638     text => 'caption', token => $token);
7639 wakaba 1.54 ## Ignore the token
7640     $token = $self->_get_next_token;
7641 wakaba 1.123 next B;
7642 wakaba 1.54 }
7643    
7644     ## generate implied end tags
7645 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7646 wakaba 1.79
7647 wakaba 1.86 pop @{$self->{open_elements}};
7648 wakaba 1.54 }
7649 wakaba 1.20
7650 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7651 wakaba 1.79
7652 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7653     text => $self->{open_elements}->[-1]->[0]
7654 wakaba 1.120 ->manakai_local_name,
7655     token => $token);
7656 wakaba 1.79 } else {
7657    
7658 wakaba 1.54 }
7659 wakaba 1.12
7660 wakaba 1.54 splice @{$self->{open_elements}}, $i;
7661 wakaba 1.31
7662 wakaba 1.54 $clear_up_to_marker->();
7663 wakaba 1.1
7664 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7665 wakaba 1.3
7666 wakaba 1.54 ## reprocess
7667 wakaba 1.123 next B;
7668 wakaba 1.54 } elsif ({
7669     body => 1, col => 1, colgroup => 1, html => 1,
7670     }->{$token->{tag_name}}) {
7671 wakaba 1.58 if ($self->{insertion_mode} & BODY_TABLE_IMS) {
7672 wakaba 1.79
7673 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7674     text => $token->{tag_name}, token => $token);
7675 wakaba 1.54 ## Ignore the token
7676     $token = $self->_get_next_token;
7677 wakaba 1.123 next B;
7678 wakaba 1.54 } else {
7679 wakaba 1.79
7680 wakaba 1.54 #
7681     }
7682     } elsif ({
7683     tbody => 1, tfoot => 1,
7684     thead => 1, tr => 1,
7685     }->{$token->{tag_name}} and
7686 wakaba 1.56 $self->{insertion_mode} == IN_CAPTION_IM) {
7687 wakaba 1.79
7688 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7689     text => $token->{tag_name}, token => $token);
7690 wakaba 1.1 ## Ignore the token
7691     $token = $self->_get_next_token;
7692 wakaba 1.123 next B;
7693 wakaba 1.54 } else {
7694 wakaba 1.79
7695 wakaba 1.54 #
7696 wakaba 1.1 }
7697 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7698     for my $entry (@{$self->{open_elements}}) {
7699 wakaba 1.121 unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
7700 wakaba 1.103
7701 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
7702 wakaba 1.103 last;
7703     }
7704     }
7705    
7706     ## Stop parsing.
7707     last B;
7708 wakaba 1.54 } else {
7709     die "$0: $token->{type}: Unknown token type";
7710 wakaba 1.1 }
7711    
7712 wakaba 1.54 $insert = $insert_to_current;
7713     #
7714 wakaba 1.58 } elsif ($self->{insertion_mode} & TABLE_IMS) {
7715 wakaba 1.60 if ($token->{type} == CHARACTER_TOKEN) {
7716 wakaba 1.95 if (not $open_tables->[-1]->[1] and # tainted
7717     $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
7718     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
7719 wakaba 1.54
7720 wakaba 1.95 unless (length $token->{data}) {
7721    
7722     $token = $self->_get_next_token;
7723 wakaba 1.123 next B;
7724 wakaba 1.95 } else {
7725    
7726     }
7727     }
7728 wakaba 1.1
7729 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table:#text', token => $token);
7730 wakaba 1.1
7731 wakaba 1.54 ## As if in body, but insert into foster parent element
7732     ## ISSUE: Spec says that "whenever a node would be inserted
7733     ## into the current node" while characters might not be
7734     ## result in a new Text node.
7735     $reconstruct_active_formatting_elements->($insert_to_foster);
7736    
7737 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
7738 wakaba 1.54 # MUST
7739     my $foster_parent_element;
7740     my $next_sibling;
7741     my $prev_sibling;
7742     OE: for (reverse 0..$#{$self->{open_elements}}) {
7743 wakaba 1.121 if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
7744 wakaba 1.54 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
7745     if (defined $parent and $parent->node_type == 1) {
7746 wakaba 1.79
7747 wakaba 1.54 $foster_parent_element = $parent;
7748     $next_sibling = $self->{open_elements}->[$_]->[0];
7749     $prev_sibling = $next_sibling->previous_sibling;
7750     } else {
7751 wakaba 1.79
7752 wakaba 1.54 $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
7753     $prev_sibling = $foster_parent_element->last_child;
7754     }
7755     last OE;
7756     }
7757     } # OE
7758     $foster_parent_element = $self->{open_elements}->[0]->[0] and
7759     $prev_sibling = $foster_parent_element->last_child
7760     unless defined $foster_parent_element;
7761     if (defined $prev_sibling and
7762     $prev_sibling->node_type == 3) {
7763 wakaba 1.79
7764 wakaba 1.54 $prev_sibling->manakai_append_text ($token->{data});
7765     } else {
7766 wakaba 1.79
7767 wakaba 1.54 $foster_parent_element->insert_before
7768     ($self->{document}->create_text_node ($token->{data}),
7769     $next_sibling);
7770     }
7771 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
7772     } else {
7773    
7774     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
7775     }
7776 wakaba 1.54
7777 wakaba 1.95 $token = $self->_get_next_token;
7778 wakaba 1.123 next B;
7779 wakaba 1.60 } elsif ($token->{type} == START_TAG_TOKEN) {
7780 wakaba 1.150 if ({
7781     tr => ($self->{insertion_mode} != IN_ROW_IM),
7782     th => 1, td => 1,
7783     }->{$token->{tag_name}}) {
7784     if ($self->{insertion_mode} == IN_TABLE_IM) {
7785     ## Clear back to table context
7786     while (not ($self->{open_elements}->[-1]->[1]
7787     & TABLE_SCOPING_EL)) {
7788    
7789     pop @{$self->{open_elements}};
7790     }
7791    
7792    
7793 wakaba 1.54 {
7794     my $el;
7795    
7796     $el = $self->{document}->create_element_ns
7797 wakaba 1.128 ($HTML_NS, [undef, 'tbody']);
7798 wakaba 1.54
7799 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7800     if defined $token->{line};
7801     $el->set_user_data (manakai_source_column => $token->{column})
7802     if defined $token->{column};
7803    
7804 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7805 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'tbody'} || 0];
7806 wakaba 1.54 }
7807    
7808 wakaba 1.150 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7809     ## reprocess in the "in table body" insertion mode...
7810     }
7811    
7812     if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
7813     unless ($token->{tag_name} eq 'tr') {
7814    
7815     $self->{parse_error}->(level => $self->{level}->{must}, type => 'missing start tag:tr', token => $token);
7816     }
7817 wakaba 1.54
7818 wakaba 1.150 ## Clear back to table body context
7819     while (not ($self->{open_elements}->[-1]->[1]
7820     & TABLE_ROWS_SCOPING_EL)) {
7821    
7822     ## ISSUE: Can this case be reached?
7823     pop @{$self->{open_elements}};
7824     }
7825 wakaba 1.54
7826 wakaba 1.56 $self->{insertion_mode} = IN_ROW_IM;
7827 wakaba 1.54 if ($token->{tag_name} eq 'tr') {
7828    
7829 wakaba 1.79
7830 wakaba 1.54 {
7831     my $el;
7832    
7833     $el = $self->{document}->create_element_ns
7834 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7835 wakaba 1.54
7836     for my $attr_name (keys %{ $token->{attributes}}) {
7837 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7838 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7839 wakaba 1.117 $attr->value ($attr_t->{value});
7840     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7841     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7842     $el->set_attribute_node_ns ($attr);
7843 wakaba 1.1 }
7844 wakaba 1.54
7845 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7846     if defined $token->{line};
7847     $el->set_user_data (manakai_source_column => $token->{column})
7848     if defined $token->{column};
7849    
7850 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7851 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7852 wakaba 1.54 }
7853    
7854 wakaba 1.122
7855 wakaba 1.54 $token = $self->_get_next_token;
7856 wakaba 1.123 next B;
7857 wakaba 1.54 } else {
7858    
7859 wakaba 1.79
7860 wakaba 1.54 {
7861     my $el;
7862    
7863     $el = $self->{document}->create_element_ns
7864 wakaba 1.128 ($HTML_NS, [undef, 'tr']);
7865 wakaba 1.54
7866 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7867     if defined $token->{line};
7868     $el->set_user_data (manakai_source_column => $token->{column})
7869     if defined $token->{column};
7870    
7871 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7872 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'tr'} || 0];
7873 wakaba 1.54 }
7874    
7875     ## reprocess in the "in row" insertion mode
7876     }
7877 wakaba 1.79 } else {
7878    
7879 wakaba 1.54 }
7880 wakaba 1.52
7881 wakaba 1.54 ## Clear back to table row context
7882 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7883     & TABLE_ROW_SCOPING_EL)) {
7884 wakaba 1.79
7885 wakaba 1.54 pop @{$self->{open_elements}};
7886     }
7887    
7888    
7889     {
7890     my $el;
7891    
7892     $el = $self->{document}->create_element_ns
7893 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7894 wakaba 1.1
7895 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
7896 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7897 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7898 wakaba 1.117 $attr->value ($attr_t->{value});
7899     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7900     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7901     $el->set_attribute_node_ns ($attr);
7902 wakaba 1.54 }
7903    
7904 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7905     if defined $token->{line};
7906     $el->set_user_data (manakai_source_column => $token->{column})
7907     if defined $token->{column};
7908    
7909 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7910 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7911 wakaba 1.54 }
7912    
7913 wakaba 1.56 $self->{insertion_mode} = IN_CELL_IM;
7914 wakaba 1.54
7915     push @$active_formatting_elements, ['#marker', ''];
7916    
7917 wakaba 1.122
7918 wakaba 1.54 $token = $self->_get_next_token;
7919 wakaba 1.123 next B;
7920 wakaba 1.54 } elsif ({
7921     caption => 1, col => 1, colgroup => 1,
7922     tbody => 1, tfoot => 1, thead => 1,
7923 wakaba 1.56 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
7924 wakaba 1.54 }->{$token->{tag_name}}) {
7925 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
7926 wakaba 1.54 ## As if </tr>
7927     ## have an element in table scope
7928     my $i;
7929     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7930     my $node = $self->{open_elements}->[$_];
7931 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
7932 wakaba 1.79
7933 wakaba 1.54 $i = $_;
7934     last INSCOPE;
7935 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7936 wakaba 1.79
7937 wakaba 1.54 last INSCOPE;
7938     }
7939     } # INSCOPE
7940 wakaba 1.79 unless (defined $i) {
7941 wakaba 1.122
7942 wakaba 1.83 ## TODO: This type is wrong.
7943 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmacthed end tag',
7944     text => $token->{tag_name}, token => $token);
7945 wakaba 1.54 ## Ignore the token
7946 wakaba 1.122
7947 wakaba 1.54 $token = $self->_get_next_token;
7948 wakaba 1.123 next B;
7949 wakaba 1.54 }
7950    
7951     ## Clear back to table row context
7952 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7953     & TABLE_ROW_SCOPING_EL)) {
7954 wakaba 1.79
7955 wakaba 1.83 ## ISSUE: Can this case be reached?
7956 wakaba 1.54 pop @{$self->{open_elements}};
7957     }
7958    
7959     pop @{$self->{open_elements}}; # tr
7960 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7961 wakaba 1.54 if ($token->{tag_name} eq 'tr') {
7962 wakaba 1.79
7963 wakaba 1.54 ## reprocess
7964 wakaba 1.122
7965 wakaba 1.123 next B;
7966 wakaba 1.54 } else {
7967 wakaba 1.79
7968 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
7969     }
7970     }
7971 wakaba 1.52
7972 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
7973 wakaba 1.54 ## have an element in table scope
7974     my $i;
7975     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7976     my $node = $self->{open_elements}->[$_];
7977 wakaba 1.121 if ($node->[1] & TABLE_ROW_GROUP_EL) {
7978 wakaba 1.79
7979 wakaba 1.54 $i = $_;
7980     last INSCOPE;
7981 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7982 wakaba 1.79
7983 wakaba 1.54 last INSCOPE;
7984     }
7985     } # INSCOPE
7986     unless (defined $i) {
7987 wakaba 1.79
7988 wakaba 1.150 ## TODO: This erorr type is wrong.
7989     $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7990     text => $token->{tag_name}, token => $token);
7991 wakaba 1.54 ## Ignore the token
7992 wakaba 1.122
7993 wakaba 1.54 $token = $self->_get_next_token;
7994 wakaba 1.123 next B;
7995 wakaba 1.54 }
7996 wakaba 1.52
7997 wakaba 1.54 ## Clear back to table body context
7998 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7999     & TABLE_ROWS_SCOPING_EL)) {
8000 wakaba 1.79
8001 wakaba 1.83 ## ISSUE: Can this state be reached?
8002 wakaba 1.54 pop @{$self->{open_elements}};
8003     }
8004    
8005     ## As if <{current node}>
8006     ## have an element in table scope
8007     ## true by definition
8008    
8009     ## Clear back to table body context
8010     ## nop by definition
8011    
8012     pop @{$self->{open_elements}};
8013 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8014 wakaba 1.54 ## reprocess in "in table" insertion mode...
8015 wakaba 1.79 } else {
8016    
8017 wakaba 1.54 }
8018 wakaba 1.51
8019 wakaba 1.54 if ($token->{tag_name} eq 'col') {
8020     ## Clear back to table context
8021 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8022     & TABLE_SCOPING_EL)) {
8023 wakaba 1.79
8024 wakaba 1.83 ## ISSUE: Can this state be reached?
8025 wakaba 1.54 pop @{$self->{open_elements}};
8026     }
8027    
8028    
8029 wakaba 1.51 {
8030     my $el;
8031    
8032     $el = $self->{document}->create_element_ns
8033 wakaba 1.128 ($HTML_NS, [undef, 'colgroup']);
8034 wakaba 1.51
8035 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8036     if defined $token->{line};
8037     $el->set_user_data (manakai_source_column => $token->{column})
8038     if defined $token->{column};
8039    
8040 wakaba 1.51 $self->{open_elements}->[-1]->[0]->append_child ($el);
8041 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'colgroup'} || 0];
8042 wakaba 1.51 }
8043    
8044 wakaba 1.56 $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
8045 wakaba 1.54 ## reprocess
8046 wakaba 1.122
8047 wakaba 1.123 next B;
8048 wakaba 1.54 } elsif ({
8049     caption => 1,
8050     colgroup => 1,
8051     tbody => 1, tfoot => 1, thead => 1,
8052     }->{$token->{tag_name}}) {
8053     ## Clear back to table context
8054 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8055     & TABLE_SCOPING_EL)) {
8056 wakaba 1.79
8057 wakaba 1.83 ## ISSUE: Can this state be reached?
8058 wakaba 1.54 pop @{$self->{open_elements}};
8059     }
8060    
8061     push @$active_formatting_elements, ['#marker', '']
8062     if $token->{tag_name} eq 'caption';
8063    
8064 wakaba 1.52
8065 wakaba 1.54 {
8066     my $el;
8067    
8068     $el = $self->{document}->create_element_ns
8069 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8070 wakaba 1.52
8071 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
8072 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8073 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8074 wakaba 1.117 $attr->value ($attr_t->{value});
8075     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8076     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8077     $el->set_attribute_node_ns ($attr);
8078 wakaba 1.52 }
8079    
8080 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8081     if defined $token->{line};
8082     $el->set_user_data (manakai_source_column => $token->{column})
8083     if defined $token->{column};
8084    
8085 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
8086 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8087 wakaba 1.54 }
8088    
8089     $self->{insertion_mode} = {
8090 wakaba 1.56 caption => IN_CAPTION_IM,
8091     colgroup => IN_COLUMN_GROUP_IM,
8092     tbody => IN_TABLE_BODY_IM,
8093     tfoot => IN_TABLE_BODY_IM,
8094     thead => IN_TABLE_BODY_IM,
8095 wakaba 1.54 }->{$token->{tag_name}};
8096 wakaba 1.52 $token = $self->_get_next_token;
8097 wakaba 1.122
8098 wakaba 1.123 next B;
8099 wakaba 1.54 } else {
8100     die "$0: in table: <>: $token->{tag_name}";
8101     }
8102     } elsif ($token->{tag_name} eq 'table') {
8103 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
8104     text => $self->{open_elements}->[-1]->[0]
8105 wakaba 1.120 ->manakai_local_name,
8106     token => $token);
8107 wakaba 1.54
8108     ## As if </table>
8109     ## have a table element in table scope
8110     my $i;
8111     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8112     my $node = $self->{open_elements}->[$_];
8113 wakaba 1.121 if ($node->[1] & TABLE_EL) {
8114 wakaba 1.79
8115 wakaba 1.54 $i = $_;
8116     last INSCOPE;
8117 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8118 wakaba 1.79
8119 wakaba 1.54 last INSCOPE;
8120     }
8121     } # INSCOPE
8122     unless (defined $i) {
8123 wakaba 1.79
8124 wakaba 1.83 ## TODO: The following is wrong, maybe.
8125 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => 'table',
8126     token => $token);
8127 wakaba 1.54 ## Ignore tokens </table><table>
8128 wakaba 1.122
8129 wakaba 1.52 $token = $self->_get_next_token;
8130 wakaba 1.123 next B;
8131 wakaba 1.52 }
8132    
8133 wakaba 1.149 ## TODO: Followings are removed from the latest spec.
8134 wakaba 1.54 ## generate implied end tags
8135 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
8136 wakaba 1.79
8137 wakaba 1.86 pop @{$self->{open_elements}};
8138 wakaba 1.54 }
8139    
8140 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
8141 wakaba 1.79
8142 wakaba 1.120 ## NOTE: |<table><tr><table>|
8143 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
8144     text => $self->{open_elements}->[-1]->[0]
8145 wakaba 1.120 ->manakai_local_name,
8146     token => $token);
8147 wakaba 1.79 } else {
8148    
8149 wakaba 1.54 }
8150    
8151     splice @{$self->{open_elements}}, $i;
8152 wakaba 1.95 pop @{$open_tables};
8153 wakaba 1.54
8154     $self->_reset_insertion_mode;
8155 wakaba 1.52
8156 wakaba 1.122 ## reprocess
8157    
8158 wakaba 1.123 next B;
8159 wakaba 1.100 } elsif ($token->{tag_name} eq 'style') {
8160     if (not $open_tables->[-1]->[1]) { # tainted
8161    
8162     ## NOTE: This is a "as if in head" code clone.
8163     $parse_rcdata->(CDATA_CONTENT_MODEL);
8164 wakaba 1.123 next B;
8165 wakaba 1.100 } else {
8166    
8167     #
8168     }
8169     } elsif ($token->{tag_name} eq 'script') {
8170     if (not $open_tables->[-1]->[1]) { # tainted
8171    
8172     ## NOTE: This is a "as if in head" code clone.
8173     $script_start_tag->();
8174 wakaba 1.123 next B;
8175 wakaba 1.100 } else {
8176    
8177     #
8178     }
8179 wakaba 1.98 } elsif ($token->{tag_name} eq 'input') {
8180     if (not $open_tables->[-1]->[1]) { # tainted
8181     if ($token->{attributes}->{type}) { ## TODO: case
8182     my $type = lc $token->{attributes}->{type}->{value};
8183     if ($type eq 'hidden') {
8184    
8185 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table',
8186     text => $token->{tag_name}, token => $token);
8187 wakaba 1.98
8188    
8189     {
8190     my $el;
8191    
8192     $el = $self->{document}->create_element_ns
8193 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8194 wakaba 1.98
8195     for my $attr_name (keys %{ $token->{attributes}}) {
8196 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8197 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8198 wakaba 1.117 $attr->value ($attr_t->{value});
8199     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8200     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8201     $el->set_attribute_node_ns ($attr);
8202 wakaba 1.98 }
8203    
8204 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8205     if defined $token->{line};
8206     $el->set_user_data (manakai_source_column => $token->{column})
8207     if defined $token->{column};
8208    
8209 wakaba 1.98 $self->{open_elements}->[-1]->[0]->append_child ($el);
8210 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8211 wakaba 1.98 }
8212    
8213    
8214     ## TODO: form element pointer
8215    
8216     pop @{$self->{open_elements}};
8217    
8218     $token = $self->_get_next_token;
8219 wakaba 1.122 delete $self->{self_closing};
8220 wakaba 1.123 next B;
8221 wakaba 1.98 } else {
8222    
8223     #
8224     }
8225     } else {
8226    
8227     #
8228     }
8229     } else {
8230    
8231     #
8232     }
8233 wakaba 1.60 } else {
8234 wakaba 1.79
8235 wakaba 1.60 #
8236     }
8237 wakaba 1.98
8238 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table', text => $token->{tag_name},
8239     token => $token);
8240 wakaba 1.98
8241     $insert = $insert_to_foster;
8242     #
8243 wakaba 1.60 } elsif ($token->{type} == END_TAG_TOKEN) {
8244 wakaba 1.54 if ($token->{tag_name} eq 'tr' and
8245 wakaba 1.56 $self->{insertion_mode} == IN_ROW_IM) {
8246 wakaba 1.54 ## have an element in table scope
8247     my $i;
8248     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8249     my $node = $self->{open_elements}->[$_];
8250 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
8251 wakaba 1.79
8252 wakaba 1.54 $i = $_;
8253     last INSCOPE;
8254 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8255 wakaba 1.79
8256 wakaba 1.54 last INSCOPE;
8257     }
8258     } # INSCOPE
8259     unless (defined $i) {
8260 wakaba 1.79
8261 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8262     text => $token->{tag_name}, token => $token);
8263 wakaba 1.54 ## Ignore the token
8264 wakaba 1.122
8265 wakaba 1.54 $token = $self->_get_next_token;
8266 wakaba 1.123 next B;
8267 wakaba 1.79 } else {
8268    
8269 wakaba 1.54 }
8270    
8271     ## Clear back to table row context
8272 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8273     & TABLE_ROW_SCOPING_EL)) {
8274 wakaba 1.79
8275 wakaba 1.83 ## ISSUE: Can this state be reached?
8276 wakaba 1.54 pop @{$self->{open_elements}};
8277     }
8278    
8279     pop @{$self->{open_elements}}; # tr
8280 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
8281 wakaba 1.54 $token = $self->_get_next_token;
8282 wakaba 1.122
8283 wakaba 1.123 next B;
8284 wakaba 1.54 } elsif ($token->{tag_name} eq 'table') {
8285 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
8286 wakaba 1.54 ## As if </tr>
8287     ## have an element in table scope
8288     my $i;
8289     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8290     my $node = $self->{open_elements}->[$_];
8291 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
8292 wakaba 1.79
8293 wakaba 1.54 $i = $_;
8294     last INSCOPE;
8295 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8296 wakaba 1.79
8297 wakaba 1.54 last INSCOPE;
8298     }
8299     } # INSCOPE
8300     unless (defined $i) {
8301 wakaba 1.79
8302 wakaba 1.83 ## TODO: The following is wrong.
8303 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8304     text => $token->{type}, token => $token);
8305 wakaba 1.54 ## Ignore the token
8306 wakaba 1.122
8307 wakaba 1.54 $token = $self->_get_next_token;
8308 wakaba 1.123 next B;
8309 wakaba 1.54 }
8310    
8311     ## Clear back to table row context
8312 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8313     & TABLE_ROW_SCOPING_EL)) {
8314 wakaba 1.79
8315 wakaba 1.83 ## ISSUE: Can this state be reached?
8316 wakaba 1.54 pop @{$self->{open_elements}};
8317     }
8318    
8319     pop @{$self->{open_elements}}; # tr
8320 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
8321 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
8322     }
8323    
8324 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
8325 wakaba 1.54 ## have an element in table scope
8326     my $i;
8327     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8328     my $node = $self->{open_elements}->[$_];
8329 wakaba 1.121 if ($node->[1] & TABLE_ROW_GROUP_EL) {
8330 wakaba 1.79
8331 wakaba 1.54 $i = $_;
8332     last INSCOPE;
8333 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8334 wakaba 1.79
8335 wakaba 1.54 last INSCOPE;
8336     }
8337     } # INSCOPE
8338     unless (defined $i) {
8339 wakaba 1.79
8340 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8341     text => $token->{tag_name}, token => $token);
8342 wakaba 1.54 ## Ignore the token
8343 wakaba 1.122
8344 wakaba 1.54 $token = $self->_get_next_token;
8345 wakaba 1.123 next B;
8346 wakaba 1.54 }
8347    
8348     ## Clear back to table body context
8349 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8350     & TABLE_ROWS_SCOPING_EL)) {
8351 wakaba 1.79
8352 wakaba 1.54 pop @{$self->{open_elements}};
8353     }
8354    
8355     ## As if <{current node}>
8356     ## have an element in table scope
8357     ## true by definition
8358    
8359     ## Clear back to table body context
8360     ## nop by definition
8361    
8362     pop @{$self->{open_elements}};
8363 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8364 wakaba 1.54 ## reprocess in the "in table" insertion mode...
8365     }
8366 wakaba 1.52
8367 wakaba 1.94 ## NOTE: </table> in the "in table" insertion mode.
8368     ## When you edit the code fragment below, please ensure that
8369     ## the code for <table> in the "in table" insertion mode
8370     ## is synced with it.
8371    
8372 wakaba 1.54 ## have a table element in table scope
8373     my $i;
8374     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8375     my $node = $self->{open_elements}->[$_];
8376 wakaba 1.121 if ($node->[1] & TABLE_EL) {
8377 wakaba 1.79
8378 wakaba 1.54 $i = $_;
8379     last INSCOPE;
8380 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8381 wakaba 1.79
8382 wakaba 1.54 last INSCOPE;
8383     }
8384     } # INSCOPE
8385     unless (defined $i) {
8386 wakaba 1.79
8387 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8388     text => $token->{tag_name}, token => $token);
8389 wakaba 1.54 ## Ignore the token
8390 wakaba 1.122
8391 wakaba 1.54 $token = $self->_get_next_token;
8392 wakaba 1.123 next B;
8393 wakaba 1.51 }
8394 wakaba 1.54
8395     splice @{$self->{open_elements}}, $i;
8396 wakaba 1.95 pop @{$open_tables};
8397 wakaba 1.54
8398     $self->_reset_insertion_mode;
8399 wakaba 1.1
8400     $token = $self->_get_next_token;
8401 wakaba 1.123 next B;
8402 wakaba 1.54 } elsif ({
8403     tbody => 1, tfoot => 1, thead => 1,
8404     }->{$token->{tag_name}} and
8405 wakaba 1.58 $self->{insertion_mode} & ROW_IMS) {
8406 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
8407 wakaba 1.54 ## have an element in table scope
8408     my $i;
8409     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8410     my $node = $self->{open_elements}->[$_];
8411 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8412 wakaba 1.79
8413 wakaba 1.54 $i = $_;
8414     last INSCOPE;
8415 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8416 wakaba 1.79
8417 wakaba 1.54 last INSCOPE;
8418     }
8419     } # INSCOPE
8420     unless (defined $i) {
8421 wakaba 1.79
8422 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8423     text => $token->{tag_name}, token => $token);
8424 wakaba 1.54 ## Ignore the token
8425 wakaba 1.122
8426 wakaba 1.54 $token = $self->_get_next_token;
8427 wakaba 1.123 next B;
8428 wakaba 1.54 }
8429    
8430     ## As if </tr>
8431     ## have an element in table scope
8432     my $i;
8433     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8434     my $node = $self->{open_elements}->[$_];
8435 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
8436 wakaba 1.79
8437 wakaba 1.54 $i = $_;
8438     last INSCOPE;
8439 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8440 wakaba 1.79
8441 wakaba 1.54 last INSCOPE;
8442     }
8443     } # INSCOPE
8444     unless (defined $i) {
8445 wakaba 1.79
8446 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8447     text => 'tr', token => $token);
8448 wakaba 1.54 ## Ignore the token
8449 wakaba 1.122
8450 wakaba 1.54 $token = $self->_get_next_token;
8451 wakaba 1.123 next B;
8452 wakaba 1.54 }
8453    
8454     ## Clear back to table row context
8455 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8456     & TABLE_ROW_SCOPING_EL)) {
8457 wakaba 1.79
8458 wakaba 1.83 ## ISSUE: Can this case be reached?
8459 wakaba 1.54 pop @{$self->{open_elements}};
8460     }
8461    
8462     pop @{$self->{open_elements}}; # tr
8463 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
8464 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
8465 wakaba 1.34 }
8466    
8467 wakaba 1.54 ## have an element in table scope
8468     my $i;
8469     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8470     my $node = $self->{open_elements}->[$_];
8471 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8472 wakaba 1.79
8473 wakaba 1.54 $i = $_;
8474     last INSCOPE;
8475 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8476 wakaba 1.79
8477 wakaba 1.54 last INSCOPE;
8478 wakaba 1.34 }
8479 wakaba 1.54 } # INSCOPE
8480     unless (defined $i) {
8481 wakaba 1.79
8482 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8483     text => $token->{tag_name}, token => $token);
8484 wakaba 1.54 ## Ignore the token
8485 wakaba 1.122
8486 wakaba 1.54 $token = $self->_get_next_token;
8487 wakaba 1.123 next B;
8488 wakaba 1.34 }
8489    
8490 wakaba 1.54 ## Clear back to table body context
8491 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8492     & TABLE_ROWS_SCOPING_EL)) {
8493 wakaba 1.79
8494 wakaba 1.83 ## ISSUE: Can this case be reached?
8495 wakaba 1.51 pop @{$self->{open_elements}};
8496 wakaba 1.25 }
8497 wakaba 1.51
8498 wakaba 1.54 pop @{$self->{open_elements}};
8499 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8500 wakaba 1.122
8501 wakaba 1.54 $token = $self->_get_next_token;
8502 wakaba 1.123 next B;
8503 wakaba 1.54 } elsif ({
8504     body => 1, caption => 1, col => 1, colgroup => 1,
8505     html => 1, td => 1, th => 1,
8506 wakaba 1.56 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
8507     tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
8508 wakaba 1.54 }->{$token->{tag_name}}) {
8509 wakaba 1.122
8510 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8511     text => $token->{tag_name}, token => $token);
8512 wakaba 1.122 ## Ignore the token
8513    
8514     $token = $self->_get_next_token;
8515 wakaba 1.123 next B;
8516 wakaba 1.60 } else {
8517 wakaba 1.79
8518 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table:/',
8519     text => $token->{tag_name}, token => $token);
8520 wakaba 1.54
8521 wakaba 1.60 $insert = $insert_to_foster;
8522     #
8523     }
8524 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8525 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
8526 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8527 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
8528 wakaba 1.103
8529 wakaba 1.104 #
8530 wakaba 1.103 } else {
8531    
8532 wakaba 1.104 #
8533 wakaba 1.103 }
8534    
8535     ## Stop parsing
8536     last B;
8537 wakaba 1.60 } else {
8538     die "$0: $token->{type}: Unknown token type";
8539     }
8540 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
8541 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8542 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
8543     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
8544     unless (length $token->{data}) {
8545 wakaba 1.79
8546 wakaba 1.54 $token = $self->_get_next_token;
8547 wakaba 1.123 next B;
8548 wakaba 1.25 }
8549 wakaba 1.54 }
8550    
8551 wakaba 1.79
8552 wakaba 1.54 #
8553 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
8554 wakaba 1.54 if ($token->{tag_name} eq 'col') {
8555    
8556 wakaba 1.79
8557 wakaba 1.25 {
8558     my $el;
8559    
8560 wakaba 1.1 $el = $self->{document}->create_element_ns
8561 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8562 wakaba 1.1
8563 wakaba 1.25 for my $attr_name (keys %{ $token->{attributes}}) {
8564 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8565 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8566 wakaba 1.117 $attr->value ($attr_t->{value});
8567     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8568     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8569     $el->set_attribute_node_ns ($attr);
8570 wakaba 1.1 }
8571    
8572 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8573     if defined $token->{line};
8574     $el->set_user_data (manakai_source_column => $token->{column})
8575     if defined $token->{column};
8576    
8577 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($el);
8578 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8579 wakaba 1.25 }
8580    
8581 wakaba 1.54 pop @{$self->{open_elements}};
8582 wakaba 1.122 delete $self->{self_closing};
8583 wakaba 1.54 $token = $self->_get_next_token;
8584 wakaba 1.123 next B;
8585 wakaba 1.54 } else {
8586 wakaba 1.79
8587 wakaba 1.54 #
8588     }
8589 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
8590 wakaba 1.54 if ($token->{tag_name} eq 'colgroup') {
8591 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
8592 wakaba 1.79
8593 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8594     text => 'colgroup', token => $token);
8595 wakaba 1.25 ## Ignore the token
8596 wakaba 1.42 $token = $self->_get_next_token;
8597 wakaba 1.123 next B;
8598 wakaba 1.24 } else {
8599 wakaba 1.79
8600 wakaba 1.54 pop @{$self->{open_elements}}; # colgroup
8601 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8602 wakaba 1.54 $token = $self->_get_next_token;
8603 wakaba 1.123 next B;
8604 wakaba 1.25 }
8605 wakaba 1.54 } elsif ($token->{tag_name} eq 'col') {
8606 wakaba 1.79
8607 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8608     text => 'col', token => $token);
8609 wakaba 1.54 ## Ignore the token
8610     $token = $self->_get_next_token;
8611 wakaba 1.123 next B;
8612 wakaba 1.54 } else {
8613 wakaba 1.79
8614 wakaba 1.54 #
8615     }
8616 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8617 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
8618 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8619    
8620     ## Stop parsing.
8621     last B;
8622     } else {
8623     ## NOTE: As if </colgroup>.
8624    
8625     pop @{$self->{open_elements}}; # colgroup
8626     $self->{insertion_mode} = IN_TABLE_IM;
8627     ## Reprocess.
8628 wakaba 1.123 next B;
8629 wakaba 1.103 }
8630     } else {
8631     die "$0: $token->{type}: Unknown token type";
8632     }
8633 wakaba 1.51
8634 wakaba 1.54 ## As if </colgroup>
8635 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
8636 wakaba 1.79
8637 wakaba 1.103 ## TODO: Wrong error type?
8638 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8639     text => 'colgroup', token => $token);
8640 wakaba 1.54 ## Ignore the token
8641 wakaba 1.122
8642 wakaba 1.54 $token = $self->_get_next_token;
8643 wakaba 1.123 next B;
8644 wakaba 1.54 } else {
8645 wakaba 1.79
8646 wakaba 1.54 pop @{$self->{open_elements}}; # colgroup
8647 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8648 wakaba 1.122
8649 wakaba 1.54 ## reprocess
8650 wakaba 1.123 next B;
8651 wakaba 1.54 }
8652 wakaba 1.101 } elsif ($self->{insertion_mode} & SELECT_IMS) {
8653 wakaba 1.60 if ($token->{type} == CHARACTER_TOKEN) {
8654 wakaba 1.79
8655 wakaba 1.60 $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
8656     $token = $self->_get_next_token;
8657 wakaba 1.123 next B;
8658 wakaba 1.60 } elsif ($token->{type} == START_TAG_TOKEN) {
8659 wakaba 1.121 if ($token->{tag_name} eq 'option') {
8660     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8661    
8662     ## As if </option>
8663     pop @{$self->{open_elements}};
8664     } else {
8665    
8666     }
8667 wakaba 1.51
8668 wakaba 1.121
8669 wakaba 1.1 {
8670     my $el;
8671    
8672     $el = $self->{document}->create_element_ns
8673 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8674 wakaba 1.1
8675     for my $attr_name (keys %{ $token->{attributes}}) {
8676 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8677 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8678 wakaba 1.117 $attr->value ($attr_t->{value});
8679     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8680     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8681     $el->set_attribute_node_ns ($attr);
8682 wakaba 1.1 }
8683    
8684 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8685     if defined $token->{line};
8686     $el->set_user_data (manakai_source_column => $token->{column})
8687     if defined $token->{column};
8688    
8689 wakaba 1.3 $self->{open_elements}->[-1]->[0]->append_child ($el);
8690 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8691 wakaba 1.1 }
8692    
8693 wakaba 1.122
8694 wakaba 1.121 $token = $self->_get_next_token;
8695 wakaba 1.123 next B;
8696 wakaba 1.121 } elsif ($token->{tag_name} eq 'optgroup') {
8697     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8698    
8699     ## As if </option>
8700     pop @{$self->{open_elements}};
8701     } else {
8702    
8703     }
8704 wakaba 1.54
8705 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
8706    
8707     ## As if </optgroup>
8708     pop @{$self->{open_elements}};
8709     } else {
8710    
8711     }
8712 wakaba 1.51
8713 wakaba 1.121
8714 wakaba 1.1 {
8715     my $el;
8716    
8717     $el = $self->{document}->create_element_ns
8718 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8719 wakaba 1.1
8720 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
8721 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8722 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8723 wakaba 1.117 $attr->value ($attr_t->{value});
8724     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8725     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8726     $el->set_attribute_node_ns ($attr);
8727 wakaba 1.54 }
8728    
8729 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8730     if defined $token->{line};
8731     $el->set_user_data (manakai_source_column => $token->{column})
8732     if defined $token->{column};
8733    
8734 wakaba 1.3 $self->{open_elements}->[-1]->[0]->append_child ($el);
8735 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8736 wakaba 1.1 }
8737    
8738 wakaba 1.122
8739 wakaba 1.121 $token = $self->_get_next_token;
8740 wakaba 1.123 next B;
8741 wakaba 1.143 } elsif ({
8742     select => 1, input => 1, textarea => 1,
8743     }->{$token->{tag_name}} or
8744 wakaba 1.101 ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
8745     {
8746     caption => 1, table => 1,
8747     tbody => 1, tfoot => 1, thead => 1,
8748     tr => 1, td => 1, th => 1,
8749     }->{$token->{tag_name}})) {
8750     ## TODO: The type below is not good - <select> is replaced by </select>
8751 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'select',
8752     token => $token);
8753 wakaba 1.101 ## NOTE: As if the token were </select> (<select> case) or
8754     ## as if there were </select> (otherwise).
8755 wakaba 1.121 ## have an element in table scope
8756     my $i;
8757     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8758     my $node = $self->{open_elements}->[$_];
8759     if ($node->[1] & SELECT_EL) {
8760    
8761     $i = $_;
8762     last INSCOPE;
8763     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8764 wakaba 1.54
8765 wakaba 1.121 last INSCOPE;
8766     }
8767     } # INSCOPE
8768     unless (defined $i) {
8769    
8770 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8771     text => 'select', token => $token);
8772 wakaba 1.121 ## Ignore the token
8773 wakaba 1.122
8774 wakaba 1.121 $token = $self->_get_next_token;
8775 wakaba 1.123 next B;
8776 wakaba 1.121 }
8777 wakaba 1.79
8778 wakaba 1.121
8779     splice @{$self->{open_elements}}, $i;
8780 wakaba 1.54
8781 wakaba 1.121 $self->_reset_insertion_mode;
8782 wakaba 1.54
8783 wakaba 1.101 if ($token->{tag_name} eq 'select') {
8784    
8785     $token = $self->_get_next_token;
8786 wakaba 1.123 next B;
8787 wakaba 1.101 } else {
8788    
8789 wakaba 1.122
8790 wakaba 1.101 ## Reprocess the token.
8791 wakaba 1.123 next B;
8792 wakaba 1.101 }
8793 wakaba 1.60 } else {
8794 wakaba 1.79
8795 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in select',
8796     text => $token->{tag_name}, token => $token);
8797 wakaba 1.60 ## Ignore the token
8798 wakaba 1.122
8799 wakaba 1.60 $token = $self->_get_next_token;
8800 wakaba 1.123 next B;
8801 wakaba 1.60 }
8802     } elsif ($token->{type} == END_TAG_TOKEN) {
8803 wakaba 1.121 if ($token->{tag_name} eq 'optgroup') {
8804     if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
8805     $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
8806    
8807     ## As if </option>
8808     splice @{$self->{open_elements}}, -2;
8809     } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
8810    
8811     pop @{$self->{open_elements}};
8812     } else {
8813    
8814 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8815     text => $token->{tag_name}, token => $token);
8816 wakaba 1.121 ## Ignore the token
8817     }
8818 wakaba 1.122
8819 wakaba 1.121 $token = $self->_get_next_token;
8820 wakaba 1.123 next B;
8821 wakaba 1.121 } elsif ($token->{tag_name} eq 'option') {
8822     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8823    
8824     pop @{$self->{open_elements}};
8825     } else {
8826    
8827 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8828     text => $token->{tag_name}, token => $token);
8829 wakaba 1.121 ## Ignore the token
8830     }
8831 wakaba 1.122
8832 wakaba 1.121 $token = $self->_get_next_token;
8833 wakaba 1.123 next B;
8834 wakaba 1.121 } elsif ($token->{tag_name} eq 'select') {
8835     ## have an element in table scope
8836     my $i;
8837     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8838     my $node = $self->{open_elements}->[$_];
8839     if ($node->[1] & SELECT_EL) {
8840    
8841     $i = $_;
8842     last INSCOPE;
8843     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8844 wakaba 1.54
8845 wakaba 1.121 last INSCOPE;
8846     }
8847     } # INSCOPE
8848     unless (defined $i) {
8849    
8850 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8851     text => $token->{tag_name}, token => $token);
8852 wakaba 1.121 ## Ignore the token
8853 wakaba 1.122
8854 wakaba 1.121 $token = $self->_get_next_token;
8855 wakaba 1.123 next B;
8856 wakaba 1.121 }
8857 wakaba 1.79
8858 wakaba 1.121
8859     splice @{$self->{open_elements}}, $i;
8860 wakaba 1.54
8861 wakaba 1.121 $self->_reset_insertion_mode;
8862 wakaba 1.54
8863 wakaba 1.122
8864 wakaba 1.121 $token = $self->_get_next_token;
8865 wakaba 1.123 next B;
8866 wakaba 1.101 } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
8867     {
8868     caption => 1, table => 1, tbody => 1,
8869     tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
8870     }->{$token->{tag_name}}) {
8871 wakaba 1.83 ## TODO: The following is wrong?
8872 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8873     text => $token->{tag_name}, token => $token);
8874 wakaba 1.121
8875     ## have an element in table scope
8876     my $i;
8877     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8878     my $node = $self->{open_elements}->[$_];
8879     if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8880    
8881     $i = $_;
8882     last INSCOPE;
8883     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8884    
8885     last INSCOPE;
8886     }
8887     } # INSCOPE
8888     unless (defined $i) {
8889    
8890     ## Ignore the token
8891 wakaba 1.122
8892 wakaba 1.121 $token = $self->_get_next_token;
8893 wakaba 1.123 next B;
8894 wakaba 1.121 }
8895 wakaba 1.54
8896 wakaba 1.121 ## As if </select>
8897     ## have an element in table scope
8898     undef $i;
8899     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8900     my $node = $self->{open_elements}->[$_];
8901     if ($node->[1] & SELECT_EL) {
8902 wakaba 1.54
8903 wakaba 1.121 $i = $_;
8904     last INSCOPE;
8905     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8906 wakaba 1.83 ## ISSUE: Can this state be reached?
8907 wakaba 1.121
8908     last INSCOPE;
8909     }
8910     } # INSCOPE
8911     unless (defined $i) {
8912    
8913 wakaba 1.83 ## TODO: The following error type is correct?
8914 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8915     text => 'select', token => $token);
8916 wakaba 1.121 ## Ignore the </select> token
8917 wakaba 1.122
8918 wakaba 1.121 $token = $self->_get_next_token; ## TODO: ok?
8919 wakaba 1.123 next B;
8920 wakaba 1.121 }
8921 wakaba 1.54
8922 wakaba 1.121
8923     splice @{$self->{open_elements}}, $i;
8924 wakaba 1.54
8925 wakaba 1.121 $self->_reset_insertion_mode;
8926 wakaba 1.54
8927 wakaba 1.122
8928 wakaba 1.121 ## reprocess
8929 wakaba 1.123 next B;
8930 wakaba 1.60 } else {
8931 wakaba 1.79
8932 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in select:/',
8933     text => $token->{tag_name}, token => $token);
8934 wakaba 1.54 ## Ignore the token
8935 wakaba 1.122
8936 wakaba 1.54 $token = $self->_get_next_token;
8937 wakaba 1.123 next B;
8938 wakaba 1.60 }
8939 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8940 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
8941 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8942    
8943 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
8944 wakaba 1.103 } else {
8945    
8946     }
8947    
8948     ## Stop parsing.
8949     last B;
8950 wakaba 1.60 } else {
8951     die "$0: $token->{type}: Unknown token type";
8952     }
8953 wakaba 1.58 } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
8954 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8955 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
8956     my $data = $1;
8957     ## As if in body
8958     $reconstruct_active_formatting_elements->($insert_to_current);
8959    
8960     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
8961    
8962     unless (length $token->{data}) {
8963 wakaba 1.79
8964 wakaba 1.54 $token = $self->_get_next_token;
8965 wakaba 1.123 next B;
8966 wakaba 1.54 }
8967     }
8968    
8969 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
8970 wakaba 1.79
8971 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:#text', token => $token);
8972 wakaba 1.54
8973 wakaba 1.85 ## Reprocess in the "after body" insertion mode.
8974 wakaba 1.79 } else {
8975    
8976 wakaba 1.54 }
8977    
8978     ## "after body" insertion mode
8979 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body:#text', token => $token);
8980 wakaba 1.54
8981 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
8982 wakaba 1.54 ## reprocess
8983 wakaba 1.123 next B;
8984 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
8985 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
8986 wakaba 1.79
8987 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html',
8988     text => $token->{tag_name}, token => $token);
8989 wakaba 1.54
8990 wakaba 1.85 ## Reprocess in the "after body" insertion mode.
8991 wakaba 1.79 } else {
8992    
8993 wakaba 1.54 }
8994    
8995     ## "after body" insertion mode
8996 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body',
8997     text => $token->{tag_name}, token => $token);
8998 wakaba 1.54
8999 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
9000 wakaba 1.122
9001 wakaba 1.54 ## reprocess
9002 wakaba 1.123 next B;
9003 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
9004 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
9005 wakaba 1.79
9006 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:/',
9007     text => $token->{tag_name}, token => $token);
9008 wakaba 1.54
9009 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
9010 wakaba 1.85 ## Reprocess in the "after body" insertion mode.
9011 wakaba 1.79 } else {
9012    
9013 wakaba 1.54 }
9014    
9015     ## "after body" insertion mode
9016     if ($token->{tag_name} eq 'html') {
9017     if (defined $self->{inner_html_node}) {
9018 wakaba 1.79
9019 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
9020     text => 'html', token => $token);
9021 wakaba 1.54 ## Ignore the token
9022     $token = $self->_get_next_token;
9023 wakaba 1.123 next B;
9024 wakaba 1.54 } else {
9025 wakaba 1.79
9026 wakaba 1.56 $self->{insertion_mode} = AFTER_HTML_BODY_IM;
9027 wakaba 1.54 $token = $self->_get_next_token;
9028 wakaba 1.123 next B;
9029 wakaba 1.54 }
9030     } else {
9031 wakaba 1.79
9032 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body:/',
9033     text => $token->{tag_name}, token => $token);
9034 wakaba 1.54
9035 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
9036 wakaba 1.54 ## reprocess
9037 wakaba 1.123 next B;
9038 wakaba 1.54 }
9039 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
9040    
9041     ## Stop parsing
9042     last B;
9043 wakaba 1.54 } else {
9044     die "$0: $token->{type}: Unknown token type";
9045     }
9046 wakaba 1.58 } elsif ($self->{insertion_mode} & FRAME_IMS) {
9047 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
9048 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
9049     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
9050    
9051     unless (length $token->{data}) {
9052 wakaba 1.79
9053 wakaba 1.54 $token = $self->_get_next_token;
9054 wakaba 1.123 next B;
9055 wakaba 1.54 }
9056     }
9057    
9058     if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
9059 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
9060 wakaba 1.79
9061 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset:#text', token => $token);
9062 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
9063 wakaba 1.79
9064 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset:#text', token => $token);
9065 wakaba 1.154 } else { # "after after frameset"
9066 wakaba 1.79
9067 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:#text', token => $token);
9068 wakaba 1.54 }
9069    
9070     ## Ignore the token.
9071     if (length $token->{data}) {
9072 wakaba 1.79
9073 wakaba 1.54 ## reprocess the rest of characters
9074     } else {
9075 wakaba 1.79
9076 wakaba 1.54 $token = $self->_get_next_token;
9077     }
9078 wakaba 1.123 next B;
9079 wakaba 1.54 }
9080    
9081     die qq[$0: Character "$token->{data}"];
9082 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
9083 wakaba 1.54 if ($token->{tag_name} eq 'frameset' and
9084 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
9085 wakaba 1.54
9086 wakaba 1.79
9087 wakaba 1.54 {
9088     my $el;
9089    
9090     $el = $self->{document}->create_element_ns
9091 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9092 wakaba 1.54
9093     for my $attr_name (keys %{ $token->{attributes}}) {
9094 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9095 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9096 wakaba 1.117 $attr->value ($attr_t->{value});
9097     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9098     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9099     $el->set_attribute_node_ns ($attr);
9100 wakaba 1.54 }
9101    
9102 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9103     if defined $token->{line};
9104     $el->set_user_data (manakai_source_column => $token->{column})
9105     if defined $token->{column};
9106    
9107 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
9108 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9109 wakaba 1.54 }
9110    
9111 wakaba 1.122
9112 wakaba 1.54 $token = $self->_get_next_token;
9113 wakaba 1.123 next B;
9114 wakaba 1.54 } elsif ($token->{tag_name} eq 'frame' and
9115 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
9116 wakaba 1.54
9117 wakaba 1.79
9118 wakaba 1.54 {
9119     my $el;
9120    
9121     $el = $self->{document}->create_element_ns
9122 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9123 wakaba 1.54
9124     for my $attr_name (keys %{ $token->{attributes}}) {
9125 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9126 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9127 wakaba 1.117 $attr->value ($attr_t->{value});
9128     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9129     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9130     $el->set_attribute_node_ns ($attr);
9131 wakaba 1.54 }
9132    
9133 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9134     if defined $token->{line};
9135     $el->set_user_data (manakai_source_column => $token->{column})
9136     if defined $token->{column};
9137    
9138 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
9139 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9140 wakaba 1.54 }
9141    
9142     pop @{$self->{open_elements}};
9143 wakaba 1.122 delete $self->{self_closing};
9144 wakaba 1.54 $token = $self->_get_next_token;
9145 wakaba 1.123 next B;
9146 wakaba 1.54 } elsif ($token->{tag_name} eq 'noframes') {
9147 wakaba 1.79
9148 wakaba 1.145 ## NOTE: As if in head.
9149 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
9150 wakaba 1.123 next B;
9151 wakaba 1.155
9152     ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
9153     ## has no parse error.
9154 wakaba 1.54 } else {
9155 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
9156 wakaba 1.79
9157 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset',
9158     text => $token->{tag_name}, token => $token);
9159 wakaba 1.154 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
9160 wakaba 1.79
9161 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset',
9162     text => $token->{tag_name}, token => $token);
9163 wakaba 1.154 } else { # "after after frameset"
9164    
9165     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after after frameset',
9166     text => $token->{tag_name}, token => $token);
9167 wakaba 1.54 }
9168     ## Ignore the token
9169 wakaba 1.122
9170 wakaba 1.54 $token = $self->_get_next_token;
9171 wakaba 1.123 next B;
9172 wakaba 1.54 }
9173 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
9174 wakaba 1.54 if ($token->{tag_name} eq 'frameset' and
9175 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
9176 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
9177 wakaba 1.54 @{$self->{open_elements}} == 1) {
9178 wakaba 1.79
9179 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
9180     text => $token->{tag_name}, token => $token);
9181 wakaba 1.54 ## Ignore the token
9182     $token = $self->_get_next_token;
9183     } else {
9184 wakaba 1.79
9185 wakaba 1.54 pop @{$self->{open_elements}};
9186     $token = $self->_get_next_token;
9187     }
9188 wakaba 1.43
9189 wakaba 1.54 if (not defined $self->{inner_html_node} and
9190 wakaba 1.121 not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
9191 wakaba 1.79
9192 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
9193 wakaba 1.79 } else {
9194    
9195 wakaba 1.54 }
9196 wakaba 1.123 next B;
9197 wakaba 1.54 } elsif ($token->{tag_name} eq 'html' and
9198 wakaba 1.56 $self->{insertion_mode} == AFTER_FRAMESET_IM) {
9199 wakaba 1.79
9200 wakaba 1.56 $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
9201 wakaba 1.54 $token = $self->_get_next_token;
9202 wakaba 1.123 next B;
9203 wakaba 1.54 } else {
9204 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
9205 wakaba 1.79
9206 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset:/',
9207     text => $token->{tag_name}, token => $token);
9208 wakaba 1.154 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
9209 wakaba 1.79
9210 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset:/',
9211     text => $token->{tag_name}, token => $token);
9212 wakaba 1.154 } else { # "after after html"
9213    
9214     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after after frameset:/',
9215     text => $token->{tag_name}, token => $token);
9216 wakaba 1.54 }
9217     ## Ignore the token
9218     $token = $self->_get_next_token;
9219 wakaba 1.123 next B;
9220 wakaba 1.54 }
9221 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
9222 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
9223 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
9224    
9225 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
9226 wakaba 1.103 } else {
9227    
9228     }
9229    
9230     ## Stop parsing
9231     last B;
9232 wakaba 1.54 } else {
9233     die "$0: $token->{type}: Unknown token type";
9234     }
9235 wakaba 1.43
9236 wakaba 1.54 ## ISSUE: An issue in spec here
9237     } else {
9238     die "$0: $self->{insertion_mode}: Unknown insertion mode";
9239     }
9240 wakaba 1.43
9241 wakaba 1.54 ## "in body" insertion mode
9242 wakaba 1.57 if ($token->{type} == START_TAG_TOKEN) {
9243 wakaba 1.54 if ($token->{tag_name} eq 'script') {
9244 wakaba 1.79
9245 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
9246 wakaba 1.100 $script_start_tag->();
9247 wakaba 1.123 next B;
9248 wakaba 1.54 } elsif ($token->{tag_name} eq 'style') {
9249 wakaba 1.79
9250 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
9251 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
9252 wakaba 1.123 next B;
9253 wakaba 1.54 } elsif ({
9254     base => 1, link => 1,
9255     }->{$token->{tag_name}}) {
9256 wakaba 1.79
9257 wakaba 1.54 ## NOTE: This is an "as if in head" code clone, only "-t" differs
9258    
9259     {
9260     my $el;
9261    
9262     $el = $self->{document}->create_element_ns
9263 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9264 wakaba 1.54
9265     for my $attr_name (keys %{ $token->{attributes}}) {
9266 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9267 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9268 wakaba 1.117 $attr->value ($attr_t->{value});
9269     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9270     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9271     $el->set_attribute_node_ns ($attr);
9272 wakaba 1.54 }
9273    
9274 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9275     if defined $token->{line};
9276     $el->set_user_data (manakai_source_column => $token->{column})
9277     if defined $token->{column};
9278    
9279 wakaba 1.54 $insert->($el);
9280 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9281 wakaba 1.54 }
9282    
9283     pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
9284 wakaba 1.122 delete $self->{self_closing};
9285 wakaba 1.54 $token = $self->_get_next_token;
9286 wakaba 1.123 next B;
9287 wakaba 1.54 } elsif ($token->{tag_name} eq 'meta') {
9288     ## NOTE: This is an "as if in head" code clone, only "-t" differs
9289    
9290     {
9291     my $el;
9292    
9293     $el = $self->{document}->create_element_ns
9294 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9295 wakaba 1.54
9296     for my $attr_name (keys %{ $token->{attributes}}) {
9297 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9298 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9299 wakaba 1.117 $attr->value ($attr_t->{value});
9300     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9301     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9302     $el->set_attribute_node_ns ($attr);
9303 wakaba 1.54 }
9304    
9305 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9306     if defined $token->{line};
9307     $el->set_user_data (manakai_source_column => $token->{column})
9308     if defined $token->{column};
9309    
9310 wakaba 1.54 $insert->($el);
9311 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9312 wakaba 1.54 }
9313    
9314 wakaba 1.68 my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
9315 wakaba 1.43
9316 wakaba 1.54 unless ($self->{confident}) {
9317 wakaba 1.131 if ($token->{attributes}->{charset}) {
9318 wakaba 1.79
9319 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
9320     ## in the {change_encoding} callback.
9321 wakaba 1.65 $self->{change_encoding}
9322 wakaba 1.112 ->($self, $token->{attributes}->{charset}->{value}, $token);
9323 wakaba 1.68
9324     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
9325     ->set_user_data (manakai_has_reference =>
9326     $token->{attributes}->{charset}
9327     ->{has_reference});
9328 wakaba 1.65 } elsif ($token->{attributes}->{content}) {
9329     if ($token->{attributes}->{content}->{value}
9330 wakaba 1.141 =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
9331 wakaba 1.71 [\x09-\x0D\x20]*=
9332 wakaba 1.54 [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
9333 wakaba 1.142 ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {
9334 wakaba 1.79
9335 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
9336     ## in the {change_encoding} callback.
9337 wakaba 1.65 $self->{change_encoding}
9338 wakaba 1.112 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
9339 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
9340     ->set_user_data (manakai_has_reference =>
9341     $token->{attributes}->{content}
9342     ->{has_reference});
9343 wakaba 1.65 }
9344 wakaba 1.54 }
9345 wakaba 1.68 } else {
9346     if ($token->{attributes}->{charset}) {
9347 wakaba 1.79
9348 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
9349     ->set_user_data (manakai_has_reference =>
9350     $token->{attributes}->{charset}
9351     ->{has_reference});
9352     }
9353 wakaba 1.69 if ($token->{attributes}->{content}) {
9354 wakaba 1.79
9355 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
9356     ->set_user_data (manakai_has_reference =>
9357     $token->{attributes}->{content}
9358     ->{has_reference});
9359     }
9360 wakaba 1.54 }
9361 wakaba 1.43
9362 wakaba 1.122 delete $self->{self_closing};
9363 wakaba 1.54 $token = $self->_get_next_token;
9364 wakaba 1.123 next B;
9365 wakaba 1.54 } elsif ($token->{tag_name} eq 'title') {
9366 wakaba 1.79
9367 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
9368 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
9369 wakaba 1.123 next B;
9370 wakaba 1.54 } elsif ($token->{tag_name} eq 'body') {
9371 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body', text => 'body', token => $token);
9372 wakaba 1.1
9373 wakaba 1.54 if (@{$self->{open_elements}} == 1 or
9374 wakaba 1.121 not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
9375 wakaba 1.79
9376 wakaba 1.54 ## Ignore the token
9377     } else {
9378     my $body_el = $self->{open_elements}->[1]->[0];
9379     for my $attr_name (keys %{$token->{attributes}}) {
9380     unless ($body_el->has_attribute_ns (undef, $attr_name)) {
9381 wakaba 1.79
9382 wakaba 1.54 $body_el->set_attribute_ns
9383     (undef, [undef, $attr_name],
9384     $token->{attributes}->{$attr_name}->{value});
9385 wakaba 1.1 }
9386 wakaba 1.54 }
9387     }
9388 wakaba 1.122
9389 wakaba 1.54 $token = $self->_get_next_token;
9390 wakaba 1.123 next B;
9391 wakaba 1.54 } elsif ({
9392     address => 1, blockquote => 1, center => 1, dir => 1,
9393 wakaba 1.85 div => 1, dl => 1, fieldset => 1,
9394     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
9395 wakaba 1.97 menu => 1, ol => 1, p => 1, ul => 1,
9396     pre => 1, listing => 1,
9397 wakaba 1.107 form => 1,
9398     table => 1,
9399     hr => 1,
9400 wakaba 1.54 }->{$token->{tag_name}}) {
9401 wakaba 1.107 if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
9402    
9403 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in form:form', token => $token);
9404 wakaba 1.107 ## Ignore the token
9405 wakaba 1.122
9406 wakaba 1.107 $token = $self->_get_next_token;
9407 wakaba 1.123 next B;
9408 wakaba 1.107 }
9409    
9410 wakaba 1.54 ## has a p element in scope
9411     INSCOPE: for (reverse @{$self->{open_elements}}) {
9412 wakaba 1.121 if ($_->[1] & P_EL) {
9413 wakaba 1.79
9414 wakaba 1.122
9415     $token->{self_closing} = $self->{self_closing};
9416     unshift @{$self->{token}}, $token;
9417     delete $self->{self_closing};
9418     # <form>
9419 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9420     line => $token->{line}, column => $token->{column}};
9421 wakaba 1.123 next B;
9422 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9423 wakaba 1.79
9424 wakaba 1.54 last INSCOPE;
9425     }
9426     } # INSCOPE
9427    
9428    
9429 wakaba 1.48 {
9430     my $el;
9431    
9432     $el = $self->{document}->create_element_ns
9433 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9434 wakaba 1.48
9435 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
9436 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9437 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9438 wakaba 1.117 $attr->value ($attr_t->{value});
9439     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9440     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9441     $el->set_attribute_node_ns ($attr);
9442 wakaba 1.54 }
9443    
9444 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9445     if defined $token->{line};
9446     $el->set_user_data (manakai_source_column => $token->{column})
9447     if defined $token->{column};
9448    
9449 wakaba 1.54 $insert->($el);
9450 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9451 wakaba 1.48 }
9452    
9453 wakaba 1.97 if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
9454 wakaba 1.122
9455 wakaba 1.54 $token = $self->_get_next_token;
9456 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
9457 wakaba 1.54 $token->{data} =~ s/^\x0A//;
9458     unless (length $token->{data}) {
9459 wakaba 1.79
9460 wakaba 1.54 $token = $self->_get_next_token;
9461 wakaba 1.79 } else {
9462    
9463 wakaba 1.54 }
9464 wakaba 1.79 } else {
9465    
9466 wakaba 1.54 }
9467 wakaba 1.107 } elsif ($token->{tag_name} eq 'form') {
9468    
9469     $self->{form_element} = $self->{open_elements}->[-1]->[0];
9470    
9471 wakaba 1.122
9472 wakaba 1.107 $token = $self->_get_next_token;
9473     } elsif ($token->{tag_name} eq 'table') {
9474    
9475     push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
9476 wakaba 1.79
9477 wakaba 1.107 $self->{insertion_mode} = IN_TABLE_IM;
9478    
9479 wakaba 1.122
9480 wakaba 1.54 $token = $self->_get_next_token;
9481 wakaba 1.107 } elsif ($token->{tag_name} eq 'hr') {
9482 wakaba 1.79
9483 wakaba 1.107 pop @{$self->{open_elements}};
9484    
9485 wakaba 1.122
9486 wakaba 1.54 $token = $self->_get_next_token;
9487     } else {
9488    
9489     $token = $self->_get_next_token;
9490     }
9491 wakaba 1.123 next B;
9492 wakaba 1.107 } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
9493 wakaba 1.54 ## has a p element in scope
9494     INSCOPE: for (reverse @{$self->{open_elements}}) {
9495 wakaba 1.121 if ($_->[1] & P_EL) {
9496 wakaba 1.79
9497 wakaba 1.122
9498     $token->{self_closing} = $self->{self_closing};
9499     unshift @{$self->{token}}, $token;
9500     delete $self->{self_closing};
9501     # <x>
9502 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9503     line => $token->{line}, column => $token->{column}};
9504 wakaba 1.123 next B;
9505 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9506 wakaba 1.79
9507 wakaba 1.54 last INSCOPE;
9508     }
9509     } # INSCOPE
9510    
9511     ## Step 1
9512     my $i = -1;
9513     my $node = $self->{open_elements}->[$i];
9514 wakaba 1.107 my $li_or_dtdd = {li => {li => 1},
9515     dt => {dt => 1, dd => 1},
9516     dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
9517 wakaba 1.54 LI: {
9518     ## Step 2
9519 wakaba 1.121 if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
9520 wakaba 1.54 if ($i != -1) {
9521 wakaba 1.79
9522 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9523     text => $self->{open_elements}->[-1]->[0]
9524 wakaba 1.120 ->manakai_local_name,
9525     token => $token);
9526 wakaba 1.79 } else {
9527    
9528 wakaba 1.54 }
9529     splice @{$self->{open_elements}}, $i;
9530     last LI;
9531 wakaba 1.79 } else {
9532    
9533 wakaba 1.54 }
9534    
9535     ## Step 3
9536 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
9537 wakaba 1.54 #not $phrasing_category->{$node->[1]} and
9538 wakaba 1.121 ($node->[1] & SPECIAL_EL or
9539     $node->[1] & SCOPING_EL) and
9540     not ($node->[1] & ADDRESS_EL) and
9541     not ($node->[1] & DIV_EL)) {
9542 wakaba 1.79
9543 wakaba 1.54 last LI;
9544     }
9545    
9546 wakaba 1.79
9547 wakaba 1.54 ## Step 4
9548     $i--;
9549     $node = $self->{open_elements}->[$i];
9550     redo LI;
9551     } # LI
9552    
9553    
9554 wakaba 1.49 {
9555     my $el;
9556    
9557     $el = $self->{document}->create_element_ns
9558 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9559 wakaba 1.49
9560     for my $attr_name (keys %{ $token->{attributes}}) {
9561 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9562 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9563 wakaba 1.117 $attr->value ($attr_t->{value});
9564     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9565     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9566     $el->set_attribute_node_ns ($attr);
9567 wakaba 1.49 }
9568    
9569 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9570     if defined $token->{line};
9571     $el->set_user_data (manakai_source_column => $token->{column})
9572     if defined $token->{column};
9573    
9574 wakaba 1.54 $insert->($el);
9575 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9576 wakaba 1.49 }
9577    
9578 wakaba 1.122
9579 wakaba 1.54 $token = $self->_get_next_token;
9580 wakaba 1.123 next B;
9581 wakaba 1.54 } elsif ($token->{tag_name} eq 'plaintext') {
9582     ## has a p element in scope
9583     INSCOPE: for (reverse @{$self->{open_elements}}) {
9584 wakaba 1.121 if ($_->[1] & P_EL) {
9585 wakaba 1.79
9586 wakaba 1.122
9587     $token->{self_closing} = $self->{self_closing};
9588     unshift @{$self->{token}}, $token;
9589     delete $self->{self_closing};
9590     # <plaintext>
9591 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9592     line => $token->{line}, column => $token->{column}};
9593 wakaba 1.123 next B;
9594 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9595 wakaba 1.79
9596 wakaba 1.54 last INSCOPE;
9597     }
9598     } # INSCOPE
9599    
9600    
9601 wakaba 1.1 {
9602     my $el;
9603    
9604     $el = $self->{document}->create_element_ns
9605 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9606 wakaba 1.1
9607 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
9608 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9609 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9610 wakaba 1.117 $attr->value ($attr_t->{value});
9611     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9612     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9613     $el->set_attribute_node_ns ($attr);
9614 wakaba 1.54 }
9615    
9616 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9617     if defined $token->{line};
9618     $el->set_user_data (manakai_source_column => $token->{column})
9619     if defined $token->{column};
9620    
9621 wakaba 1.54 $insert->($el);
9622 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9623 wakaba 1.1 }
9624    
9625 wakaba 1.54
9626     $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
9627    
9628 wakaba 1.122
9629 wakaba 1.54 $token = $self->_get_next_token;
9630 wakaba 1.123 next B;
9631 wakaba 1.54 } elsif ($token->{tag_name} eq 'a') {
9632     AFE: for my $i (reverse 0..$#$active_formatting_elements) {
9633     my $node = $active_formatting_elements->[$i];
9634 wakaba 1.121 if ($node->[1] & A_EL) {
9635 wakaba 1.79
9636 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in a:a', token => $token);
9637 wakaba 1.54
9638 wakaba 1.122
9639     $token->{self_closing} = $self->{self_closing};
9640     unshift @{$self->{token}}, $token;
9641     delete $self->{self_closing};
9642     # <a>
9643 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'a',
9644     line => $token->{line}, column => $token->{column}};
9645 wakaba 1.111 $formatting_end_tag->($token);
9646 wakaba 1.54
9647     AFE2: for (reverse 0..$#$active_formatting_elements) {
9648     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
9649 wakaba 1.79
9650 wakaba 1.54 splice @$active_formatting_elements, $_, 1;
9651     last AFE2;
9652 wakaba 1.49 }
9653 wakaba 1.54 } # AFE2
9654     OE: for (reverse 0..$#{$self->{open_elements}}) {
9655     if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
9656 wakaba 1.79
9657 wakaba 1.54 splice @{$self->{open_elements}}, $_, 1;
9658     last OE;
9659 wakaba 1.49 }
9660 wakaba 1.54 } # OE
9661     last AFE;
9662     } elsif ($node->[0] eq '#marker') {
9663 wakaba 1.79
9664 wakaba 1.54 last AFE;
9665     }
9666     } # AFE
9667    
9668     $reconstruct_active_formatting_elements->($insert_to_current);
9669 wakaba 1.49
9670 wakaba 1.54
9671     {
9672     my $el;
9673    
9674     $el = $self->{document}->create_element_ns
9675 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9676 wakaba 1.54
9677     for my $attr_name (keys %{ $token->{attributes}}) {
9678 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9679 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9680 wakaba 1.117 $attr->value ($attr_t->{value});
9681     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9682     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9683     $el->set_attribute_node_ns ($attr);
9684 wakaba 1.54 }
9685    
9686 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9687     if defined $token->{line};
9688     $el->set_user_data (manakai_source_column => $token->{column})
9689     if defined $token->{column};
9690    
9691 wakaba 1.54 $insert->($el);
9692 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9693 wakaba 1.54 }
9694    
9695     push @$active_formatting_elements, $self->{open_elements}->[-1];
9696 wakaba 1.48
9697 wakaba 1.122
9698 wakaba 1.54 $token = $self->_get_next_token;
9699 wakaba 1.123 next B;
9700 wakaba 1.54 } elsif ($token->{tag_name} eq 'nobr') {
9701     $reconstruct_active_formatting_elements->($insert_to_current);
9702 wakaba 1.1
9703 wakaba 1.54 ## has a |nobr| element in scope
9704     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9705     my $node = $self->{open_elements}->[$_];
9706 wakaba 1.121 if ($node->[1] & NOBR_EL) {
9707 wakaba 1.79
9708 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in nobr:nobr', token => $token);
9709 wakaba 1.122
9710     $token->{self_closing} = $self->{self_closing};
9711     unshift @{$self->{token}}, $token;
9712     delete $self->{self_closing};
9713     # <nobr>
9714 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
9715     line => $token->{line}, column => $token->{column}};
9716 wakaba 1.123 next B;
9717 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9718 wakaba 1.79
9719 wakaba 1.54 last INSCOPE;
9720     }
9721     } # INSCOPE
9722    
9723    
9724     {
9725     my $el;
9726    
9727     $el = $self->{document}->create_element_ns
9728 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9729 wakaba 1.54
9730     for my $attr_name (keys %{ $token->{attributes}}) {
9731 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9732 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9733 wakaba 1.117 $attr->value ($attr_t->{value});
9734     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9735     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9736     $el->set_attribute_node_ns ($attr);
9737 wakaba 1.54 }
9738    
9739 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9740     if defined $token->{line};
9741     $el->set_user_data (manakai_source_column => $token->{column})
9742     if defined $token->{column};
9743    
9744 wakaba 1.54 $insert->($el);
9745 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9746 wakaba 1.54 }
9747    
9748     push @$active_formatting_elements, $self->{open_elements}->[-1];
9749    
9750 wakaba 1.122
9751 wakaba 1.54 $token = $self->_get_next_token;
9752 wakaba 1.123 next B;
9753 wakaba 1.54 } elsif ($token->{tag_name} eq 'button') {
9754     ## has a button element in scope
9755     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9756     my $node = $self->{open_elements}->[$_];
9757 wakaba 1.121 if ($node->[1] & BUTTON_EL) {
9758 wakaba 1.79
9759 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in button:button', token => $token);
9760 wakaba 1.122
9761     $token->{self_closing} = $self->{self_closing};
9762     unshift @{$self->{token}}, $token;
9763     delete $self->{self_closing};
9764     # <button>
9765 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'button',
9766     line => $token->{line}, column => $token->{column}};
9767 wakaba 1.123 next B;
9768 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9769 wakaba 1.79
9770 wakaba 1.54 last INSCOPE;
9771 wakaba 1.1 }
9772 wakaba 1.54 } # INSCOPE
9773    
9774     $reconstruct_active_formatting_elements->($insert_to_current);
9775    
9776    
9777     {
9778     my $el;
9779    
9780     $el = $self->{document}->create_element_ns
9781 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9782 wakaba 1.54
9783     for my $attr_name (keys %{ $token->{attributes}}) {
9784 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9785 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9786 wakaba 1.117 $attr->value ($attr_t->{value});
9787     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9788     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9789     $el->set_attribute_node_ns ($attr);
9790 wakaba 1.54 }
9791    
9792 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9793     if defined $token->{line};
9794     $el->set_user_data (manakai_source_column => $token->{column})
9795     if defined $token->{column};
9796    
9797 wakaba 1.54 $insert->($el);
9798 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9799 wakaba 1.54 }
9800    
9801 wakaba 1.86
9802     ## TODO: associate with $self->{form_element} if defined
9803    
9804 wakaba 1.54 push @$active_formatting_elements, ['#marker', ''];
9805 wakaba 1.1
9806 wakaba 1.122
9807 wakaba 1.54 $token = $self->_get_next_token;
9808 wakaba 1.123 next B;
9809 wakaba 1.102 } elsif ({
9810 wakaba 1.107 xmp => 1,
9811     iframe => 1,
9812     noembed => 1,
9813 wakaba 1.145 noframes => 1, ## NOTE: This is an "as if in head" code clone.
9814 wakaba 1.107 noscript => 0, ## TODO: 1 if scripting is enabled
9815 wakaba 1.102 }->{$token->{tag_name}}) {
9816 wakaba 1.107 if ($token->{tag_name} eq 'xmp') {
9817    
9818     $reconstruct_active_formatting_elements->($insert_to_current);
9819     } else {
9820    
9821 wakaba 1.1 }
9822 wakaba 1.107 ## NOTE: There is an "as if in body" code clone.
9823 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
9824 wakaba 1.123 next B;
9825 wakaba 1.54 } elsif ($token->{tag_name} eq 'isindex') {
9826 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'isindex', token => $token);
9827 wakaba 1.54
9828     if (defined $self->{form_element}) {
9829 wakaba 1.79
9830 wakaba 1.54 ## Ignore the token
9831 wakaba 1.122 ## NOTE: Not acknowledged.
9832 wakaba 1.54 $token = $self->_get_next_token;
9833 wakaba 1.123 next B;
9834 wakaba 1.54 } else {
9835 wakaba 1.144 delete $self->{self_closing};
9836    
9837 wakaba 1.54 my $at = $token->{attributes};
9838     my $form_attrs;
9839     $form_attrs->{action} = $at->{action} if $at->{action};
9840     my $prompt_attr = $at->{prompt};
9841     $at->{name} = {name => 'name', value => 'isindex'};
9842     delete $at->{action};
9843     delete $at->{prompt};
9844     my @tokens = (
9845 wakaba 1.57 {type => START_TAG_TOKEN, tag_name => 'form',
9846 wakaba 1.112 attributes => $form_attrs,
9847     line => $token->{line}, column => $token->{column}},
9848     {type => START_TAG_TOKEN, tag_name => 'hr',
9849     line => $token->{line}, column => $token->{column}},
9850     {type => START_TAG_TOKEN, tag_name => 'p',
9851     line => $token->{line}, column => $token->{column}},
9852     {type => START_TAG_TOKEN, tag_name => 'label',
9853     line => $token->{line}, column => $token->{column}},
9854 wakaba 1.54 );
9855     if ($prompt_attr) {
9856 wakaba 1.79
9857 wakaba 1.112 push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
9858 wakaba 1.116 #line => $token->{line}, column => $token->{column},
9859     };
9860 wakaba 1.1 } else {
9861 wakaba 1.79
9862 wakaba 1.57 push @tokens, {type => CHARACTER_TOKEN,
9863 wakaba 1.112 data => 'This is a searchable index. Insert your search keywords here: ',
9864 wakaba 1.116 #line => $token->{line}, column => $token->{column},
9865     }; # SHOULD
9866 wakaba 1.54 ## TODO: make this configurable
9867 wakaba 1.1 }
9868 wakaba 1.54 push @tokens,
9869 wakaba 1.112 {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
9870     line => $token->{line}, column => $token->{column}},
9871 wakaba 1.57 #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
9872 wakaba 1.112 {type => END_TAG_TOKEN, tag_name => 'label',
9873     line => $token->{line}, column => $token->{column}},
9874     {type => END_TAG_TOKEN, tag_name => 'p',
9875     line => $token->{line}, column => $token->{column}},
9876     {type => START_TAG_TOKEN, tag_name => 'hr',
9877     line => $token->{line}, column => $token->{column}},
9878     {type => END_TAG_TOKEN, tag_name => 'form',
9879     line => $token->{line}, column => $token->{column}};
9880 wakaba 1.54 unshift @{$self->{token}}, (@tokens);
9881 wakaba 1.122 $token = $self->_get_next_token;
9882 wakaba 1.123 next B;
9883 wakaba 1.54 }
9884     } elsif ($token->{tag_name} eq 'textarea') {
9885     my $tag_name = $token->{tag_name};
9886     my $el;
9887    
9888     $el = $self->{document}->create_element_ns
9889 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9890 wakaba 1.54
9891     for my $attr_name (keys %{ $token->{attributes}}) {
9892 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9893 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9894 wakaba 1.117 $attr->value ($attr_t->{value});
9895     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9896     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9897     $el->set_attribute_node_ns ($attr);
9898 wakaba 1.54 }
9899    
9900 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9901     if defined $token->{line};
9902     $el->set_user_data (manakai_source_column => $token->{column})
9903     if defined $token->{column};
9904    
9905 wakaba 1.54
9906     ## TODO: $self->{form_element} if defined
9907     $self->{content_model} = RCDATA_CONTENT_MODEL;
9908     delete $self->{escape}; # MUST
9909    
9910     $insert->($el);
9911    
9912     my $text = '';
9913 wakaba 1.122
9914 wakaba 1.54 $token = $self->_get_next_token;
9915 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
9916 wakaba 1.54 $token->{data} =~ s/^\x0A//;
9917 wakaba 1.53 unless (length $token->{data}) {
9918 wakaba 1.79
9919 wakaba 1.53 $token = $self->_get_next_token;
9920 wakaba 1.79 } else {
9921    
9922 wakaba 1.53 }
9923 wakaba 1.79 } else {
9924    
9925 wakaba 1.53 }
9926 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) {
9927 wakaba 1.79
9928 wakaba 1.54 $text .= $token->{data};
9929     $token = $self->_get_next_token;
9930     }
9931     if (length $text) {
9932 wakaba 1.79
9933 wakaba 1.54 $el->manakai_append_text ($text);
9934     }
9935    
9936     $self->{content_model} = PCDATA_CONTENT_MODEL;
9937    
9938 wakaba 1.57 if ($token->{type} == END_TAG_TOKEN and
9939 wakaba 1.54 $token->{tag_name} eq $tag_name) {
9940 wakaba 1.79
9941 wakaba 1.54 ## Ignore the token
9942     } else {
9943 wakaba 1.79
9944 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in RCDATA:#eof', token => $token);
9945 wakaba 1.54 }
9946     $token = $self->_get_next_token;
9947 wakaba 1.123 next B;
9948 wakaba 1.149 } elsif ($token->{tag_name} eq 'rt' or
9949     $token->{tag_name} eq 'rp') {
9950     ## has a |ruby| element in scope
9951     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9952     my $node = $self->{open_elements}->[$_];
9953     if ($node->[1] & RUBY_EL) {
9954    
9955     ## generate implied end tags
9956     while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
9957    
9958     pop @{$self->{open_elements}};
9959     }
9960     unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
9961    
9962 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9963     text => $self->{open_elements}->[-1]->[0]
9964 wakaba 1.149 ->manakai_local_name,
9965     token => $token);
9966     pop @{$self->{open_elements}}
9967     while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
9968     }
9969     last INSCOPE;
9970     } elsif ($node->[1] & SCOPING_EL) {
9971    
9972     last INSCOPE;
9973     }
9974     } # INSCOPE
9975    
9976    
9977     {
9978     my $el;
9979    
9980     $el = $self->{document}->create_element_ns
9981     ($HTML_NS, [undef, $token->{tag_name}]);
9982    
9983     for my $attr_name (keys %{ $token->{attributes}}) {
9984     my $attr_t = $token->{attributes}->{$attr_name};
9985     my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9986     $attr->value ($attr_t->{value});
9987     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9988     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9989     $el->set_attribute_node_ns ($attr);
9990     }
9991    
9992     $el->set_user_data (manakai_source_line => $token->{line})
9993     if defined $token->{line};
9994     $el->set_user_data (manakai_source_column => $token->{column})
9995     if defined $token->{column};
9996    
9997     $insert->($el);
9998     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9999     }
10000    
10001    
10002    
10003     $token = $self->_get_next_token;
10004     redo B;
10005 wakaba 1.123 } elsif ($token->{tag_name} eq 'math' or
10006     $token->{tag_name} eq 'svg') {
10007     $reconstruct_active_formatting_elements->($insert_to_current);
10008 wakaba 1.128
10009 wakaba 1.152 ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
10010    
10011 wakaba 1.128 ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
10012    
10013     ## "adjust foreign attributes" - done in insert-element-f
10014 wakaba 1.123
10015    
10016     {
10017     my $el;
10018    
10019     $el = $self->{document}->create_element_ns
10020 wakaba 1.128 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, [undef, $token->{tag_name}]);
10021 wakaba 1.123
10022     for my $attr_name (keys %{ $token->{attributes}}) {
10023     my $attr_t = $token->{attributes}->{$attr_name};
10024 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (
10025     @{
10026     $foreign_attr_xname->{$attr_name} ||
10027     [undef, [undef,
10028 wakaba 1.152 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS) eq $SVG_NS ?
10029 wakaba 1.128 ($svg_attr_name->{$attr_name} || $attr_name) :
10030 wakaba 1.152 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS) eq $MML_NS ?
10031     ($attr_name eq 'definitionurl' ?
10032     'definitionURL' : $attr_name) :
10033 wakaba 1.128 $attr_name]]
10034     }
10035     );
10036 wakaba 1.123 $attr->value ($attr_t->{value});
10037     $attr->set_user_data (manakai_source_line => $attr_t->{line});
10038     $attr->set_user_data (manakai_source_column => $attr_t->{column});
10039     $el->set_attribute_node_ns ($attr);
10040     }
10041    
10042     $el->set_user_data (manakai_source_line => $token->{line})
10043     if defined $token->{line};
10044     $el->set_user_data (manakai_source_column => $token->{column})
10045     if defined $token->{column};
10046    
10047     $insert->($el);
10048     push @{$self->{open_elements}}, [$el, ($el_category_f->{$token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS}->{ $token->{tag_name}} || 0) | FOREIGN_EL];
10049 wakaba 1.128
10050     if ( $token->{attributes}->{xmlns} and $token->{attributes}->{xmlns}->{value} ne ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS)) {
10051 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad namespace', token => $token);
10052 wakaba 1.128 ## TODO: Error type documentation
10053     }
10054 wakaba 1.123 }
10055    
10056    
10057     if ($self->{self_closing}) {
10058     pop @{$self->{open_elements}};
10059     delete $self->{self_closing};
10060     } else {
10061    
10062     $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
10063     ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
10064     ## mode, "in body" (not "in foreign content") secondary insertion
10065     ## mode, maybe.
10066     }
10067    
10068     $token = $self->_get_next_token;
10069     next B;
10070 wakaba 1.54 } elsif ({
10071     caption => 1, col => 1, colgroup => 1, frame => 1,
10072     frameset => 1, head => 1, option => 1, optgroup => 1,
10073     tbody => 1, td => 1, tfoot => 1, th => 1,
10074     thead => 1, tr => 1,
10075     }->{$token->{tag_name}}) {
10076 wakaba 1.79
10077 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body',
10078     text => $token->{tag_name}, token => $token);
10079 wakaba 1.54 ## Ignore the token
10080 wakaba 1.122 ## NOTE: |<col/>| or |<frame/>| here is an error.
10081 wakaba 1.54 $token = $self->_get_next_token;
10082 wakaba 1.123 next B;
10083 wakaba 1.54
10084     ## ISSUE: An issue on HTML5 new elements in the spec.
10085     } else {
10086 wakaba 1.108 if ($token->{tag_name} eq 'image') {
10087    
10088 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'image', token => $token);
10089 wakaba 1.108 $token->{tag_name} = 'img';
10090     } else {
10091    
10092     }
10093    
10094     ## NOTE: There is an "as if <br>" code clone.
10095 wakaba 1.54 $reconstruct_active_formatting_elements->($insert_to_current);
10096 wakaba 1.53
10097 wakaba 1.54
10098     {
10099     my $el;
10100    
10101     $el = $self->{document}->create_element_ns
10102 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
10103 wakaba 1.54
10104     for my $attr_name (keys %{ $token->{attributes}}) {
10105 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
10106 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
10107 wakaba 1.117 $attr->value ($attr_t->{value});
10108     $attr->set_user_data (manakai_source_line => $attr_t->{line});
10109     $attr->set_user_data (manakai_source_column => $attr_t->{column});
10110     $el->set_attribute_node_ns ($attr);
10111 wakaba 1.53 }
10112 wakaba 1.54
10113 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
10114     if defined $token->{line};
10115     $el->set_user_data (manakai_source_column => $token->{column})
10116     if defined $token->{column};
10117    
10118 wakaba 1.54 $insert->($el);
10119 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
10120 wakaba 1.54 }
10121    
10122 wakaba 1.107
10123 wakaba 1.108 if ({
10124     applet => 1, marquee => 1, object => 1,
10125     }->{$token->{tag_name}}) {
10126    
10127     push @$active_formatting_elements, ['#marker', ''];
10128 wakaba 1.122
10129 wakaba 1.108 } elsif ({
10130     b => 1, big => 1, em => 1, font => 1, i => 1,
10131     s => 1, small => 1, strile => 1,
10132     strong => 1, tt => 1, u => 1,
10133     }->{$token->{tag_name}}) {
10134    
10135     push @$active_formatting_elements, $self->{open_elements}->[-1];
10136 wakaba 1.122
10137 wakaba 1.108 } elsif ($token->{tag_name} eq 'input') {
10138    
10139     ## TODO: associate with $self->{form_element} if defined
10140     pop @{$self->{open_elements}};
10141 wakaba 1.122 delete $self->{self_closing};
10142 wakaba 1.108 } elsif ({
10143     area => 1, basefont => 1, bgsound => 1, br => 1,
10144     embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
10145     #image => 1,
10146     }->{$token->{tag_name}}) {
10147    
10148     pop @{$self->{open_elements}};
10149 wakaba 1.122 delete $self->{self_closing};
10150 wakaba 1.108 } elsif ($token->{tag_name} eq 'select') {
10151 wakaba 1.107 ## TODO: associate with $self->{form_element} if defined
10152    
10153     if ($self->{insertion_mode} & TABLE_IMS or
10154     $self->{insertion_mode} & BODY_TABLE_IMS or
10155     $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
10156    
10157     $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
10158     } else {
10159    
10160     $self->{insertion_mode} = IN_SELECT_IM;
10161     }
10162 wakaba 1.122
10163 wakaba 1.108 } else {
10164    
10165 wakaba 1.107 }
10166 wakaba 1.53
10167 wakaba 1.54 $token = $self->_get_next_token;
10168 wakaba 1.123 next B;
10169 wakaba 1.54 }
10170 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
10171 wakaba 1.54 if ($token->{tag_name} eq 'body') {
10172 wakaba 1.105 ## has a |body| element in scope
10173     my $i;
10174 wakaba 1.109 INSCOPE: {
10175     for (reverse @{$self->{open_elements}}) {
10176 wakaba 1.121 if ($_->[1] & BODY_EL) {
10177 wakaba 1.109
10178     $i = $_;
10179     last INSCOPE;
10180 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
10181 wakaba 1.109
10182     last;
10183     }
10184 wakaba 1.54 }
10185 wakaba 1.109
10186 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'start tag not allowed',
10187     text => $token->{tag_name}, token => $token);
10188 wakaba 1.105 ## NOTE: Ignore the token.
10189 wakaba 1.54 $token = $self->_get_next_token;
10190 wakaba 1.123 next B;
10191 wakaba 1.109 } # INSCOPE
10192 wakaba 1.105
10193     for (@{$self->{open_elements}}) {
10194 wakaba 1.121 unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
10195 wakaba 1.105
10196 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10197     text => $_->[0]->manakai_local_name,
10198 wakaba 1.120 token => $token);
10199 wakaba 1.105 last;
10200     } else {
10201    
10202     }
10203     }
10204    
10205     $self->{insertion_mode} = AFTER_BODY_IM;
10206     $token = $self->_get_next_token;
10207 wakaba 1.123 next B;
10208 wakaba 1.54 } elsif ($token->{tag_name} eq 'html') {
10209 wakaba 1.120 ## TODO: Update this code. It seems that the code below is not
10210     ## up-to-date, though it has same effect as speced.
10211 wakaba 1.121 if (@{$self->{open_elements}} > 1 and
10212     $self->{open_elements}->[1]->[1] & BODY_EL) {
10213 wakaba 1.54 ## ISSUE: There is an issue in the spec.
10214 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
10215 wakaba 1.79
10216 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10217     text => $self->{open_elements}->[1]->[0]
10218 wakaba 1.120 ->manakai_local_name,
10219     token => $token);
10220 wakaba 1.79 } else {
10221    
10222 wakaba 1.54 }
10223 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
10224 wakaba 1.54 ## reprocess
10225 wakaba 1.123 next B;
10226 wakaba 1.54 } else {
10227 wakaba 1.79
10228 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10229     text => $token->{tag_name}, token => $token);
10230 wakaba 1.54 ## Ignore the token
10231     $token = $self->_get_next_token;
10232 wakaba 1.123 next B;
10233 wakaba 1.53 }
10234 wakaba 1.54 } elsif ({
10235     address => 1, blockquote => 1, center => 1, dir => 1,
10236     div => 1, dl => 1, fieldset => 1, listing => 1,
10237     menu => 1, ol => 1, pre => 1, ul => 1,
10238     dd => 1, dt => 1, li => 1,
10239 wakaba 1.102 applet => 1, button => 1, marquee => 1, object => 1,
10240 wakaba 1.54 }->{$token->{tag_name}}) {
10241     ## has an element in scope
10242     my $i;
10243     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
10244     my $node = $self->{open_elements}->[$_];
10245 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
10246 wakaba 1.79
10247 wakaba 1.54 $i = $_;
10248 wakaba 1.87 last INSCOPE;
10249 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10250 wakaba 1.79
10251 wakaba 1.54 last INSCOPE;
10252     }
10253     } # INSCOPE
10254 wakaba 1.89
10255     unless (defined $i) { # has an element in scope
10256    
10257 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10258     text => $token->{tag_name}, token => $token);
10259 wakaba 1.154 ## NOTE: Ignore the token.
10260 wakaba 1.89 } else {
10261     ## Step 1. generate implied end tags
10262     while ({
10263 wakaba 1.149 ## END_TAG_OPTIONAL_EL
10264 wakaba 1.89 dd => ($token->{tag_name} ne 'dd'),
10265     dt => ($token->{tag_name} ne 'dt'),
10266     li => ($token->{tag_name} ne 'li'),
10267     p => 1,
10268 wakaba 1.149 rt => 1,
10269     rp => 1,
10270 wakaba 1.121 }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
10271 wakaba 1.89
10272     pop @{$self->{open_elements}};
10273     }
10274    
10275     ## Step 2.
10276 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10277     ne $token->{tag_name}) {
10278 wakaba 1.79
10279 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10280     text => $self->{open_elements}->[-1]->[0]
10281 wakaba 1.120 ->manakai_local_name,
10282     token => $token);
10283 wakaba 1.1 } else {
10284 wakaba 1.79
10285 wakaba 1.1 }
10286 wakaba 1.89
10287     ## Step 3.
10288 wakaba 1.54 splice @{$self->{open_elements}}, $i;
10289 wakaba 1.89
10290     ## Step 4.
10291     $clear_up_to_marker->()
10292     if {
10293 wakaba 1.102 applet => 1, button => 1, marquee => 1, object => 1,
10294 wakaba 1.89 }->{$token->{tag_name}};
10295 wakaba 1.54 }
10296     $token = $self->_get_next_token;
10297 wakaba 1.123 next B;
10298 wakaba 1.54 } elsif ($token->{tag_name} eq 'form') {
10299 wakaba 1.92 undef $self->{form_element};
10300    
10301 wakaba 1.54 ## has an element in scope
10302 wakaba 1.92 my $i;
10303 wakaba 1.54 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
10304     my $node = $self->{open_elements}->[$_];
10305 wakaba 1.121 if ($node->[1] & FORM_EL) {
10306 wakaba 1.79
10307 wakaba 1.92 $i = $_;
10308 wakaba 1.54 last INSCOPE;
10309 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10310 wakaba 1.79
10311 wakaba 1.54 last INSCOPE;
10312 wakaba 1.36 }
10313 wakaba 1.54 } # INSCOPE
10314 wakaba 1.92
10315     unless (defined $i) { # has an element in scope
10316 wakaba 1.79
10317 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10318     text => $token->{tag_name}, token => $token);
10319 wakaba 1.154 ## NOTE: Ignore the token.
10320 wakaba 1.54 } else {
10321 wakaba 1.92 ## Step 1. generate implied end tags
10322 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
10323 wakaba 1.92
10324     pop @{$self->{open_elements}};
10325     }
10326    
10327     ## Step 2.
10328 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10329     ne $token->{tag_name}) {
10330 wakaba 1.92
10331 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10332     text => $self->{open_elements}->[-1]->[0]
10333 wakaba 1.120 ->manakai_local_name,
10334     token => $token);
10335 wakaba 1.92 } else {
10336    
10337     }
10338 wakaba 1.79
10339 wakaba 1.92 ## Step 3.
10340     splice @{$self->{open_elements}}, $i;
10341 wakaba 1.36 }
10342    
10343 wakaba 1.54 $token = $self->_get_next_token;
10344 wakaba 1.123 next B;
10345 wakaba 1.54 } elsif ({
10346     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
10347     }->{$token->{tag_name}}) {
10348     ## has an element in scope
10349     my $i;
10350     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
10351     my $node = $self->{open_elements}->[$_];
10352 wakaba 1.121 if ($node->[1] & HEADING_EL) {
10353 wakaba 1.79
10354 wakaba 1.54 $i = $_;
10355     last INSCOPE;
10356 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10357 wakaba 1.79
10358 wakaba 1.54 last INSCOPE;
10359 wakaba 1.53 }
10360 wakaba 1.54 } # INSCOPE
10361 wakaba 1.93
10362     unless (defined $i) { # has an element in scope
10363 wakaba 1.79
10364 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10365     text => $token->{tag_name}, token => $token);
10366 wakaba 1.154 ## NOTE: Ignore the token.
10367 wakaba 1.79 } else {
10368 wakaba 1.93 ## Step 1. generate implied end tags
10369 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
10370 wakaba 1.93
10371     pop @{$self->{open_elements}};
10372     }
10373 wakaba 1.79
10374 wakaba 1.93 ## Step 2.
10375 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10376     ne $token->{tag_name}) {
10377 wakaba 1.93
10378 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10379     text => $token->{tag_name}, token => $token);
10380 wakaba 1.93 } else {
10381    
10382     }
10383    
10384     ## Step 3.
10385     splice @{$self->{open_elements}}, $i;
10386 wakaba 1.53 }
10387    
10388 wakaba 1.54 $token = $self->_get_next_token;
10389 wakaba 1.123 next B;
10390 wakaba 1.87 } elsif ($token->{tag_name} eq 'p') {
10391     ## has an element in scope
10392     my $i;
10393     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
10394     my $node = $self->{open_elements}->[$_];
10395 wakaba 1.121 if ($node->[1] & P_EL) {
10396 wakaba 1.87
10397     $i = $_;
10398 wakaba 1.88 last INSCOPE;
10399 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10400 wakaba 1.87
10401     last INSCOPE;
10402     }
10403     } # INSCOPE
10404 wakaba 1.91
10405     if (defined $i) {
10406 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10407     ne $token->{tag_name}) {
10408 wakaba 1.87
10409 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10410     text => $self->{open_elements}->[-1]->[0]
10411 wakaba 1.120 ->manakai_local_name,
10412     token => $token);
10413 wakaba 1.87 } else {
10414    
10415     }
10416 wakaba 1.91
10417 wakaba 1.87 splice @{$self->{open_elements}}, $i;
10418     } else {
10419    
10420 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10421     text => $token->{tag_name}, token => $token);
10422 wakaba 1.91
10423    
10424 wakaba 1.87 ## As if <p>, then reprocess the current token
10425     my $el;
10426    
10427     $el = $self->{document}->create_element_ns
10428 wakaba 1.128 ($HTML_NS, [undef, 'p']);
10429 wakaba 1.87
10430 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
10431     if defined $token->{line};
10432     $el->set_user_data (manakai_source_column => $token->{column})
10433     if defined $token->{column};
10434    
10435 wakaba 1.87 $insert->($el);
10436 wakaba 1.91 ## NOTE: Not inserted into |$self->{open_elements}|.
10437 wakaba 1.87 }
10438 wakaba 1.91
10439 wakaba 1.87 $token = $self->_get_next_token;
10440 wakaba 1.123 next B;
10441 wakaba 1.54 } elsif ({
10442     a => 1,
10443     b => 1, big => 1, em => 1, font => 1, i => 1,
10444     nobr => 1, s => 1, small => 1, strile => 1,
10445     strong => 1, tt => 1, u => 1,
10446     }->{$token->{tag_name}}) {
10447 wakaba 1.79
10448 wakaba 1.111 $formatting_end_tag->($token);
10449 wakaba 1.123 next B;
10450 wakaba 1.54 } elsif ($token->{tag_name} eq 'br') {
10451 wakaba 1.79
10452 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10453     text => 'br', token => $token);
10454 wakaba 1.53
10455 wakaba 1.54 ## As if <br>
10456     $reconstruct_active_formatting_elements->($insert_to_current);
10457    
10458     my $el;
10459    
10460 wakaba 1.1 $el = $self->{document}->create_element_ns
10461 wakaba 1.128 ($HTML_NS, [undef, 'br']);
10462 wakaba 1.1
10463 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
10464     if defined $token->{line};
10465     $el->set_user_data (manakai_source_column => $token->{column})
10466     if defined $token->{column};
10467    
10468 wakaba 1.54 $insert->($el);
10469    
10470     ## Ignore the token.
10471     $token = $self->_get_next_token;
10472 wakaba 1.123 next B;
10473 wakaba 1.54 } elsif ({
10474     caption => 1, col => 1, colgroup => 1, frame => 1,
10475     frameset => 1, head => 1, option => 1, optgroup => 1,
10476     tbody => 1, td => 1, tfoot => 1, th => 1,
10477     thead => 1, tr => 1,
10478     area => 1, basefont => 1, bgsound => 1,
10479     embed => 1, hr => 1, iframe => 1, image => 1,
10480     img => 1, input => 1, isindex => 1, noembed => 1,
10481     noframes => 1, param => 1, select => 1, spacer => 1,
10482     table => 1, textarea => 1, wbr => 1,
10483     noscript => 0, ## TODO: if scripting is enabled
10484     }->{$token->{tag_name}}) {
10485 wakaba 1.79
10486 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10487     text => $token->{tag_name}, token => $token);
10488 wakaba 1.54 ## Ignore the token
10489     $token = $self->_get_next_token;
10490 wakaba 1.123 next B;
10491 wakaba 1.54
10492     ## ISSUE: Issue on HTML5 new elements in spec
10493    
10494     } else {
10495     ## Step 1
10496     my $node_i = -1;
10497     my $node = $self->{open_elements}->[$node_i];
10498    
10499     ## Step 2
10500     S2: {
10501 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
10502 wakaba 1.54 ## Step 1
10503     ## generate implied end tags
10504 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
10505 wakaba 1.79
10506 wakaba 1.149 ## NOTE: |<ruby><rt></ruby>|.
10507     ## ISSUE: <ruby><rt></rt> will also take this code path,
10508     ## which seems wrong.
10509 wakaba 1.86 pop @{$self->{open_elements}};
10510 wakaba 1.149 $node_i++;
10511 wakaba 1.54 }
10512    
10513     ## Step 2
10514 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10515     ne $token->{tag_name}) {
10516 wakaba 1.79
10517 wakaba 1.60 ## NOTE: <x><y></x>
10518 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10519     text => $self->{open_elements}->[-1]->[0]
10520 wakaba 1.120 ->manakai_local_name,
10521     token => $token);
10522 wakaba 1.79 } else {
10523    
10524 wakaba 1.54 }
10525    
10526     ## Step 3
10527 wakaba 1.149 splice @{$self->{open_elements}}, $node_i if $node_i < 0;
10528 wakaba 1.53
10529 wakaba 1.36 $token = $self->_get_next_token;
10530 wakaba 1.54 last S2;
10531 wakaba 1.1 } else {
10532 wakaba 1.54 ## Step 3
10533 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
10534 wakaba 1.54 #not $phrasing_category->{$node->[1]} and
10535 wakaba 1.121 ($node->[1] & SPECIAL_EL or
10536     $node->[1] & SCOPING_EL)) {
10537 wakaba 1.79
10538 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10539     text => $token->{tag_name}, token => $token);
10540 wakaba 1.54 ## Ignore the token
10541     $token = $self->_get_next_token;
10542     last S2;
10543     }
10544 wakaba 1.79
10545    
10546 wakaba 1.1 }
10547 wakaba 1.54
10548     ## Step 4
10549     $node_i--;
10550     $node = $self->{open_elements}->[$node_i];
10551    
10552     ## Step 5;
10553     redo S2;
10554     } # S2
10555 wakaba 1.123 next B;
10556 wakaba 1.1 }
10557     }
10558 wakaba 1.123 next B;
10559     } continue { # B
10560     if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
10561     ## NOTE: The code below is executed in cases where it does not have
10562     ## to be, but it it is harmless even in those cases.
10563     ## has an element in scope
10564     INSCOPE: {
10565     for (reverse 0..$#{$self->{open_elements}}) {
10566     my $node = $self->{open_elements}->[$_];
10567     if ($node->[1] & FOREIGN_EL) {
10568     last INSCOPE;
10569     } elsif ($node->[1] & SCOPING_EL) {
10570     last;
10571     }
10572     }
10573    
10574     ## NOTE: No foreign element in scope.
10575     $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
10576     } # INSCOPE
10577     }
10578 wakaba 1.1 } # B
10579    
10580     ## Stop parsing # MUST
10581    
10582     ## TODO: script stuffs
10583 wakaba 1.3 } # _tree_construct_main
10584    
10585 wakaba 1.173 sub set_inner_html ($$$$;$) {
10586 wakaba 1.3 my $class = shift;
10587     my $node = shift;
10588 wakaba 1.173 #my $s = \$_[0];
10589 wakaba 1.3 my $onerror = $_[1];
10590 wakaba 1.158 my $get_wrapper = $_[2] || sub ($) { return $_[0] };
10591 wakaba 1.3
10592 wakaba 1.65 ## ISSUE: Should {confident} be true?
10593    
10594 wakaba 1.3 my $nt = $node->node_type;
10595     if ($nt == 9) {
10596     # MUST
10597    
10598     ## Step 1 # MUST
10599     ## TODO: If the document has an active parser, ...
10600     ## ISSUE: There is an issue in the spec.
10601    
10602     ## Step 2 # MUST
10603     my @cn = @{$node->child_nodes};
10604     for (@cn) {
10605     $node->remove_child ($_);
10606     }
10607    
10608     ## Step 3, 4, 5 # MUST
10609 wakaba 1.173 $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
10610 wakaba 1.3 } elsif ($nt == 1) {
10611     ## TODO: If non-html element
10612    
10613     ## NOTE: Most of this code is copied from |parse_string|
10614    
10615 wakaba 1.158 ## TODO: Support for $get_wrapper
10616    
10617 wakaba 1.3 ## Step 1 # MUST
10618 wakaba 1.14 my $this_doc = $node->owner_document;
10619     my $doc = $this_doc->implementation->create_document;
10620 wakaba 1.18 $doc->manakai_is_html (1);
10621 wakaba 1.3 my $p = $class->new;
10622     $p->{document} = $doc;
10623    
10624 wakaba 1.84 ## Step 8 # MUST
10625 wakaba 1.3 my $i = 0;
10626 wakaba 1.119 $p->{line_prev} = $p->{line} = 1;
10627     $p->{column_prev} = $p->{column} = 0;
10628 wakaba 1.173 require Whatpm::Charset::DecodeHandle;
10629     my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
10630     $input = $get_wrapper->($input);
10631 wakaba 1.76 $p->{set_next_char} = sub {
10632 wakaba 1.3 my $self = shift;
10633 wakaba 1.14
10634 wakaba 1.174 my $char = '';
10635 wakaba 1.173 if (defined $self->{next_next_char}) {
10636     $char = $self->{next_next_char};
10637     delete $self->{next_next_char};
10638 wakaba 1.174 $self->{next_char} = ord $char;
10639 wakaba 1.173 } else {
10640 wakaba 1.176 $self->{char_buffer} = '';
10641     $self->{char_buffer_pos} = 0;
10642    
10643     my $count = $input->manakai_read_until
10644 wakaba 1.178 ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
10645     $self->{char_buffer_pos});
10646 wakaba 1.176 if ($count) {
10647     $self->{line_prev} = $self->{line};
10648     $self->{column_prev} = $self->{column};
10649     $self->{column}++;
10650     $self->{next_char}
10651     = ord substr ($self->{char_buffer},
10652     $self->{char_buffer_pos}++, 1);
10653     return;
10654     }
10655    
10656 wakaba 1.174 if ($input->read ($char, 1)) {
10657     $self->{next_char} = ord $char;
10658     } else {
10659     $self->{next_char} = -1;
10660     return;
10661     }
10662 wakaba 1.173 }
10663 wakaba 1.119
10664     ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
10665     $p->{column}++;
10666 wakaba 1.4
10667 wakaba 1.76 if ($self->{next_char} == 0x000A) { # LF
10668 wakaba 1.119 $p->{line}++;
10669     $p->{column} = 0;
10670 wakaba 1.79
10671 wakaba 1.76 } elsif ($self->{next_char} == 0x000D) { # CR
10672 wakaba 1.173 ## TODO: support for abort/streaming
10673 wakaba 1.174 my $next = '';
10674     if ($input->read ($next, 1) and $next ne "\x0A") {
10675 wakaba 1.173 $self->{next_next_char} = $next;
10676     }
10677 wakaba 1.76 $self->{next_char} = 0x000A; # LF # MUST
10678 wakaba 1.119 $p->{line}++;
10679     $p->{column} = 0;
10680 wakaba 1.79
10681 wakaba 1.76 } elsif ($self->{next_char} == 0x0000) { # NULL
10682 wakaba 1.79
10683 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'NULL');
10684 wakaba 1.76 $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
10685 wakaba 1.3 }
10686     };
10687 wakaba 1.167
10688 wakaba 1.168 $p->{read_until} = sub {
10689 wakaba 1.173 #my ($scalar, $specials_range, $offset) = @_;
10690     return 0 if defined $p->{next_next_char};
10691 wakaba 1.176
10692 wakaba 1.178 my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
10693 wakaba 1.176 my $offset = $_[2] || 0;
10694    
10695     if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
10696     pos ($p->{char_buffer}) = $p->{char_buffer_pos};
10697     if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
10698     substr ($_[0], $offset)
10699     = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
10700     my $count = $+[0] - $-[0];
10701     if ($count) {
10702     $p->{column} += $count;
10703     $p->{char_buffer_pos} += $count;
10704     $p->{line_prev} = $p->{line};
10705     $p->{column_prev} = $p->{column} - 1;
10706     $p->{prev_char} = [-1, -1, -1];
10707     $p->{next_char} = -1;
10708     }
10709     return $count;
10710     } else {
10711     return 0;
10712     }
10713     } else {
10714     my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
10715     if ($count) {
10716     $p->{column} += $count;
10717     $p->{column_prev} += $count;
10718     $p->{prev_char} = [-1, -1, -1];
10719     $p->{next_char} = -1;
10720     }
10721     return $count;
10722 wakaba 1.173 }
10723     }; # $p->{read_until}
10724 wakaba 1.167
10725 wakaba 1.3 my $ponerror = $onerror || sub {
10726     my (%opt) = @_;
10727 wakaba 1.119 my $line = $opt{line};
10728     my $column = $opt{column};
10729     if (defined $opt{token} and defined $opt{token}->{line}) {
10730     $line = $opt{token}->{line};
10731     $column = $opt{token}->{column};
10732     }
10733     warn "Parse error ($opt{type}) at line $line column $column\n";
10734 wakaba 1.3 };
10735     $p->{parse_error} = sub {
10736 wakaba 1.119 $ponerror->(line => $p->{line}, column => $p->{column}, @_);
10737 wakaba 1.3 };
10738    
10739 wakaba 1.174 my $char_onerror = sub {
10740     my (undef, $type, %opt) = @_;
10741     $ponerror->(layer => 'encode',
10742     line => $p->{line}, column => $p->{column} + 1,
10743     %opt, type => $type);
10744     }; # $char_onerror
10745     $input->onerror ($char_onerror);
10746    
10747 wakaba 1.3 $p->_initialize_tokenizer;
10748     $p->_initialize_tree_constructor;
10749    
10750     ## Step 2
10751 wakaba 1.71 my $node_ln = $node->manakai_local_name;
10752 wakaba 1.41 $p->{content_model} = {
10753     title => RCDATA_CONTENT_MODEL,
10754     textarea => RCDATA_CONTENT_MODEL,
10755     style => CDATA_CONTENT_MODEL,
10756     script => CDATA_CONTENT_MODEL,
10757     xmp => CDATA_CONTENT_MODEL,
10758     iframe => CDATA_CONTENT_MODEL,
10759     noembed => CDATA_CONTENT_MODEL,
10760     noframes => CDATA_CONTENT_MODEL,
10761     noscript => CDATA_CONTENT_MODEL,
10762     plaintext => PLAINTEXT_CONTENT_MODEL,
10763     }->{$node_ln};
10764     $p->{content_model} = PCDATA_CONTENT_MODEL
10765     unless defined $p->{content_model};
10766     ## ISSUE: What is "the name of the element"? local name?
10767 wakaba 1.3
10768 wakaba 1.121 $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
10769     ## TODO: Foreign element OK?
10770 wakaba 1.3
10771 wakaba 1.84 ## Step 3
10772 wakaba 1.3 my $root = $doc->create_element_ns
10773     ('http://www.w3.org/1999/xhtml', [undef, 'html']);
10774    
10775 wakaba 1.84 ## Step 4 # MUST
10776 wakaba 1.3 $doc->append_child ($root);
10777    
10778 wakaba 1.84 ## Step 5 # MUST
10779 wakaba 1.121 push @{$p->{open_elements}}, [$root, $el_category->{html}];
10780 wakaba 1.3
10781     undef $p->{head_element};
10782    
10783 wakaba 1.84 ## Step 6 # MUST
10784 wakaba 1.3 $p->_reset_insertion_mode;
10785    
10786 wakaba 1.84 ## Step 7 # MUST
10787 wakaba 1.3 my $anode = $node;
10788     AN: while (defined $anode) {
10789     if ($anode->node_type == 1) {
10790     my $nsuri = $anode->namespace_uri;
10791     if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
10792 wakaba 1.71 if ($anode->manakai_local_name eq 'form') {
10793 wakaba 1.79
10794 wakaba 1.3 $p->{form_element} = $anode;
10795     last AN;
10796     }
10797     }
10798     }
10799     $anode = $anode->parent_node;
10800     } # AN
10801    
10802 wakaba 1.84 ## Step 9 # MUST
10803 wakaba 1.3 {
10804     my $self = $p;
10805     $token = $self->_get_next_token;
10806     }
10807     $p->_tree_construction_main;
10808    
10809 wakaba 1.84 ## Step 10 # MUST
10810 wakaba 1.3 my @cn = @{$node->child_nodes};
10811     for (@cn) {
10812     $node->remove_child ($_);
10813     }
10814     ## ISSUE: mutation events? read-only?
10815    
10816 wakaba 1.84 ## Step 11 # MUST
10817 wakaba 1.3 @cn = @{$root->child_nodes};
10818     for (@cn) {
10819 wakaba 1.14 $this_doc->adopt_node ($_);
10820 wakaba 1.3 $node->append_child ($_);
10821     }
10822 wakaba 1.14 ## ISSUE: mutation events?
10823 wakaba 1.3
10824     $p->_terminate_tree_constructor;
10825 wakaba 1.119
10826     delete $p->{parse_error}; # delete loop
10827 wakaba 1.3 } else {
10828     die "$0: |set_inner_html| is not defined for node of type $nt";
10829     }
10830     } # set_inner_html
10831    
10832     } # tree construction stage
10833 wakaba 1.1
10834 wakaba 1.65 package Whatpm::HTML::RestartParser;
10835     push our @ISA, 'Error';
10836    
10837 wakaba 1.1 1;
10838 wakaba 1.178 # $Date: 2008/09/15 02:54:12 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24