/[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.180 - (hide annotations) (download)
Mon Sep 15 09:02:27 2008 UTC (17 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.179: +55 -463 lines
++ whatpm/Whatpm/ChangeLog	15 Sep 2008 09:02:14 -0000
	* HTML.pm.src, mkhtmlparser.pl: Replace |{prev_char}|
	by |{s_kwd}| in DATA_STATE.

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

1 wakaba 1.2 package Whatpm::HTML;
2 wakaba 1.1 use strict;
3 wakaba 1.180 our $VERSION=do{my @r=(q$Revision: 1.183 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 wakaba 1.65 use Error qw(:try);
5 wakaba 1.1
6 wakaba 1.179 ## NOTE: This module don't check all HTML5 parse errors; character
7     ## encoding related parse errors are expected to be handled by relevant
8     ## modules.
9     ## Parse errors for control characters that are not allowed in HTML5
10     ## documents, for surrogate code points, and for noncharacter code
11     ## points, as well as U+FFFD substitions for characters whose code points
12     ## is higher than U+10FFFF may be detected by combining the parser with
13     ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14     ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15     ## WebHACC::Language::HTML module in the WebHACC package).
16    
17 wakaba 1.18 ## ISSUE:
18     ## var doc = implementation.createDocument (null, null, null);
19     ## doc.write ('');
20     ## alert (doc.compatMode);
21 wakaba 1.1
22 wakaba 1.136 require IO::Handle;
23    
24 wakaba 1.123 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25     my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26     my $SVG_NS = q<http://www.w3.org/2000/svg>;
27     my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28     my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29     my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    
31 wakaba 1.121 sub A_EL () { 0b1 }
32     sub ADDRESS_EL () { 0b10 }
33     sub BODY_EL () { 0b100 }
34     sub BUTTON_EL () { 0b1000 }
35     sub CAPTION_EL () { 0b10000 }
36     sub DD_EL () { 0b100000 }
37     sub DIV_EL () { 0b1000000 }
38     sub DT_EL () { 0b10000000 }
39     sub FORM_EL () { 0b100000000 }
40     sub FORMATTING_EL () { 0b1000000000 }
41     sub FRAMESET_EL () { 0b10000000000 }
42     sub HEADING_EL () { 0b100000000000 }
43     sub HTML_EL () { 0b1000000000000 }
44     sub LI_EL () { 0b10000000000000 }
45     sub NOBR_EL () { 0b100000000000000 }
46     sub OPTION_EL () { 0b1000000000000000 }
47     sub OPTGROUP_EL () { 0b10000000000000000 }
48     sub P_EL () { 0b100000000000000000 }
49     sub SELECT_EL () { 0b1000000000000000000 }
50     sub TABLE_EL () { 0b10000000000000000000 }
51     sub TABLE_CELL_EL () { 0b100000000000000000000 }
52     sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53     sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54     sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55     sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56 wakaba 1.123 sub FOREIGN_EL () { 0b10000000000000000000000000 }
57     sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58     sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59 wakaba 1.149 sub RUBY_EL () { 0b10000000000000000000000000000 }
60     sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61 wakaba 1.121
62     sub TABLE_ROWS_EL () {
63     TABLE_EL |
64     TABLE_ROW_EL |
65     TABLE_ROW_GROUP_EL
66     }
67    
68 wakaba 1.149 ## NOTE: Used in "generate implied end tags" algorithm.
69     ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL
70     ## is used in "generate implied end tags" implementation (search for the
71     ## function mae).
72 wakaba 1.121 sub END_TAG_OPTIONAL_EL () {
73     DD_EL |
74     DT_EL |
75     LI_EL |
76 wakaba 1.149 P_EL |
77     RUBY_COMPONENT_EL
78 wakaba 1.121 }
79    
80 wakaba 1.149 ## NOTE: Used in </body> and EOF algorithms.
81 wakaba 1.121 sub ALL_END_TAG_OPTIONAL_EL () {
82 wakaba 1.149 DD_EL |
83     DT_EL |
84     LI_EL |
85     P_EL |
86    
87 wakaba 1.121 BODY_EL |
88     HTML_EL |
89     TABLE_CELL_EL |
90     TABLE_ROW_EL |
91     TABLE_ROW_GROUP_EL
92     }
93    
94     sub SCOPING_EL () {
95     BUTTON_EL |
96     CAPTION_EL |
97     HTML_EL |
98     TABLE_EL |
99     TABLE_CELL_EL |
100     MISC_SCOPING_EL
101     }
102    
103     sub TABLE_SCOPING_EL () {
104     HTML_EL |
105     TABLE_EL
106     }
107    
108     sub TABLE_ROWS_SCOPING_EL () {
109     HTML_EL |
110     TABLE_ROW_GROUP_EL
111     }
112    
113     sub TABLE_ROW_SCOPING_EL () {
114     HTML_EL |
115     TABLE_ROW_EL
116     }
117    
118     sub SPECIAL_EL () {
119     ADDRESS_EL |
120     BODY_EL |
121     DIV_EL |
122 wakaba 1.149
123     DD_EL |
124     DT_EL |
125     LI_EL |
126     P_EL |
127    
128 wakaba 1.121 FORM_EL |
129     FRAMESET_EL |
130     HEADING_EL |
131     OPTION_EL |
132     OPTGROUP_EL |
133     SELECT_EL |
134     TABLE_ROW_EL |
135     TABLE_ROW_GROUP_EL |
136     MISC_SPECIAL_EL
137     }
138    
139     my $el_category = {
140     a => A_EL | FORMATTING_EL,
141     address => ADDRESS_EL,
142     applet => MISC_SCOPING_EL,
143     area => MISC_SPECIAL_EL,
144     b => FORMATTING_EL,
145     base => MISC_SPECIAL_EL,
146     basefont => MISC_SPECIAL_EL,
147     bgsound => MISC_SPECIAL_EL,
148     big => FORMATTING_EL,
149     blockquote => MISC_SPECIAL_EL,
150     body => BODY_EL,
151     br => MISC_SPECIAL_EL,
152     button => BUTTON_EL,
153     caption => CAPTION_EL,
154     center => MISC_SPECIAL_EL,
155     col => MISC_SPECIAL_EL,
156     colgroup => MISC_SPECIAL_EL,
157     dd => DD_EL,
158     dir => MISC_SPECIAL_EL,
159     div => DIV_EL,
160     dl => MISC_SPECIAL_EL,
161     dt => DT_EL,
162     em => FORMATTING_EL,
163     embed => MISC_SPECIAL_EL,
164     fieldset => MISC_SPECIAL_EL,
165     font => FORMATTING_EL,
166     form => FORM_EL,
167     frame => MISC_SPECIAL_EL,
168     frameset => FRAMESET_EL,
169     h1 => HEADING_EL,
170     h2 => HEADING_EL,
171     h3 => HEADING_EL,
172     h4 => HEADING_EL,
173     h5 => HEADING_EL,
174     h6 => HEADING_EL,
175     head => MISC_SPECIAL_EL,
176     hr => MISC_SPECIAL_EL,
177     html => HTML_EL,
178     i => FORMATTING_EL,
179     iframe => MISC_SPECIAL_EL,
180     img => MISC_SPECIAL_EL,
181     input => MISC_SPECIAL_EL,
182     isindex => MISC_SPECIAL_EL,
183     li => LI_EL,
184     link => MISC_SPECIAL_EL,
185     listing => MISC_SPECIAL_EL,
186     marquee => MISC_SCOPING_EL,
187     menu => MISC_SPECIAL_EL,
188     meta => MISC_SPECIAL_EL,
189     nobr => NOBR_EL | FORMATTING_EL,
190     noembed => MISC_SPECIAL_EL,
191     noframes => MISC_SPECIAL_EL,
192     noscript => MISC_SPECIAL_EL,
193     object => MISC_SCOPING_EL,
194     ol => MISC_SPECIAL_EL,
195     optgroup => OPTGROUP_EL,
196     option => OPTION_EL,
197     p => P_EL,
198     param => MISC_SPECIAL_EL,
199     plaintext => MISC_SPECIAL_EL,
200     pre => MISC_SPECIAL_EL,
201 wakaba 1.149 rp => RUBY_COMPONENT_EL,
202     rt => RUBY_COMPONENT_EL,
203     ruby => RUBY_EL,
204 wakaba 1.121 s => FORMATTING_EL,
205     script => MISC_SPECIAL_EL,
206     select => SELECT_EL,
207     small => FORMATTING_EL,
208     spacer => MISC_SPECIAL_EL,
209     strike => FORMATTING_EL,
210     strong => FORMATTING_EL,
211     style => MISC_SPECIAL_EL,
212     table => TABLE_EL,
213     tbody => TABLE_ROW_GROUP_EL,
214     td => TABLE_CELL_EL,
215     textarea => MISC_SPECIAL_EL,
216     tfoot => TABLE_ROW_GROUP_EL,
217     th => TABLE_CELL_EL,
218     thead => TABLE_ROW_GROUP_EL,
219     title => MISC_SPECIAL_EL,
220     tr => TABLE_ROW_EL,
221     tt => FORMATTING_EL,
222     u => FORMATTING_EL,
223     ul => MISC_SPECIAL_EL,
224     wbr => MISC_SPECIAL_EL,
225     };
226    
227 wakaba 1.123 my $el_category_f = {
228     $MML_NS => {
229     'annotation-xml' => MML_AXML_EL,
230     mi => FOREIGN_FLOW_CONTENT_EL,
231     mo => FOREIGN_FLOW_CONTENT_EL,
232     mn => FOREIGN_FLOW_CONTENT_EL,
233     ms => FOREIGN_FLOW_CONTENT_EL,
234     mtext => FOREIGN_FLOW_CONTENT_EL,
235     },
236     $SVG_NS => {
237 wakaba 1.128 foreignObject => FOREIGN_FLOW_CONTENT_EL,
238 wakaba 1.123 desc => FOREIGN_FLOW_CONTENT_EL,
239     title => FOREIGN_FLOW_CONTENT_EL,
240     },
241     ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
242     };
243    
244 wakaba 1.128 my $svg_attr_name = {
245 wakaba 1.143 attributename => 'attributeName',
246 wakaba 1.128 attributetype => 'attributeType',
247     basefrequency => 'baseFrequency',
248     baseprofile => 'baseProfile',
249     calcmode => 'calcMode',
250     clippathunits => 'clipPathUnits',
251     contentscripttype => 'contentScriptType',
252     contentstyletype => 'contentStyleType',
253     diffuseconstant => 'diffuseConstant',
254     edgemode => 'edgeMode',
255     externalresourcesrequired => 'externalResourcesRequired',
256     filterres => 'filterRes',
257     filterunits => 'filterUnits',
258     glyphref => 'glyphRef',
259     gradienttransform => 'gradientTransform',
260     gradientunits => 'gradientUnits',
261     kernelmatrix => 'kernelMatrix',
262     kernelunitlength => 'kernelUnitLength',
263     keypoints => 'keyPoints',
264     keysplines => 'keySplines',
265     keytimes => 'keyTimes',
266     lengthadjust => 'lengthAdjust',
267     limitingconeangle => 'limitingConeAngle',
268     markerheight => 'markerHeight',
269     markerunits => 'markerUnits',
270     markerwidth => 'markerWidth',
271     maskcontentunits => 'maskContentUnits',
272     maskunits => 'maskUnits',
273     numoctaves => 'numOctaves',
274     pathlength => 'pathLength',
275     patterncontentunits => 'patternContentUnits',
276     patterntransform => 'patternTransform',
277     patternunits => 'patternUnits',
278     pointsatx => 'pointsAtX',
279     pointsaty => 'pointsAtY',
280     pointsatz => 'pointsAtZ',
281     preservealpha => 'preserveAlpha',
282     preserveaspectratio => 'preserveAspectRatio',
283     primitiveunits => 'primitiveUnits',
284     refx => 'refX',
285     refy => 'refY',
286     repeatcount => 'repeatCount',
287     repeatdur => 'repeatDur',
288     requiredextensions => 'requiredExtensions',
289 wakaba 1.143 requiredfeatures => 'requiredFeatures',
290 wakaba 1.128 specularconstant => 'specularConstant',
291     specularexponent => 'specularExponent',
292     spreadmethod => 'spreadMethod',
293     startoffset => 'startOffset',
294     stddeviation => 'stdDeviation',
295     stitchtiles => 'stitchTiles',
296     surfacescale => 'surfaceScale',
297     systemlanguage => 'systemLanguage',
298     tablevalues => 'tableValues',
299     targetx => 'targetX',
300     targety => 'targetY',
301     textlength => 'textLength',
302     viewbox => 'viewBox',
303     viewtarget => 'viewTarget',
304     xchannelselector => 'xChannelSelector',
305     ychannelselector => 'yChannelSelector',
306     zoomandpan => 'zoomAndPan',
307     };
308    
309     my $foreign_attr_xname = {
310     'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
311     'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
312     'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
313     'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
314     'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
315     'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
316     'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
317     'xml:base' => [$XML_NS, ['xml', 'base']],
318     'xml:lang' => [$XML_NS, ['xml', 'lang']],
319     'xml:space' => [$XML_NS, ['xml', 'space']],
320     'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
321     'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
322     };
323    
324 wakaba 1.129 ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
325    
326 wakaba 1.4 my $c1_entity_char = {
327 wakaba 1.9 0x80 => 0x20AC,
328     0x81 => 0xFFFD,
329     0x82 => 0x201A,
330     0x83 => 0x0192,
331     0x84 => 0x201E,
332     0x85 => 0x2026,
333     0x86 => 0x2020,
334     0x87 => 0x2021,
335     0x88 => 0x02C6,
336     0x89 => 0x2030,
337     0x8A => 0x0160,
338     0x8B => 0x2039,
339     0x8C => 0x0152,
340     0x8D => 0xFFFD,
341     0x8E => 0x017D,
342     0x8F => 0xFFFD,
343     0x90 => 0xFFFD,
344     0x91 => 0x2018,
345     0x92 => 0x2019,
346     0x93 => 0x201C,
347     0x94 => 0x201D,
348     0x95 => 0x2022,
349     0x96 => 0x2013,
350     0x97 => 0x2014,
351     0x98 => 0x02DC,
352     0x99 => 0x2122,
353     0x9A => 0x0161,
354     0x9B => 0x203A,
355     0x9C => 0x0153,
356     0x9D => 0xFFFD,
357     0x9E => 0x017E,
358     0x9F => 0x0178,
359 wakaba 1.4 }; # $c1_entity_char
360 wakaba 1.1
361 wakaba 1.65 sub parse_byte_string ($$$$;$) {
362 wakaba 1.135 my $self = shift;
363     my $charset_name = shift;
364     open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
365     return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
366     } # parse_byte_string
367    
368 wakaba 1.158 sub parse_byte_stream ($$$$;$$) {
369     # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
370 wakaba 1.65 my $self = ref $_[0] ? shift : shift->new;
371 wakaba 1.130 my $charset_name = shift;
372 wakaba 1.135 my $byte_stream = $_[0];
373 wakaba 1.130
374 wakaba 1.131 my $onerror = $_[2] || sub {
375     my (%opt) = @_;
376     warn "Parse error ($opt{type})\n";
377     };
378     $self->{parse_error} = $onerror; # updated later by parse_char_string
379    
380 wakaba 1.158 my $get_wrapper = $_[3] || sub ($) {
381     return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
382     };
383    
384 wakaba 1.130 ## HTML5 encoding sniffing algorithm
385     require Message::Charset::Info;
386     my $charset;
387 wakaba 1.133 my $buffer;
388     my ($char_stream, $e_status);
389 wakaba 1.130
390     SNIFFING: {
391 wakaba 1.156 ## NOTE: By setting |allow_fallback| option true when the
392     ## |get_decode_handle| method is invoked, we ignore what the HTML5
393     ## spec requires, i.e. unsupported encoding should be ignored.
394     ## TODO: We should not do this unless the parser is invoked
395     ## in the conformance checking mode, in which this behavior
396     ## would be useful.
397 wakaba 1.130
398     ## Step 1
399     if (defined $charset_name) {
400 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
401     ## TODO: Is this ok? Transfer protocol's parameter should be
402     ## interpreted in its semantics?
403 wakaba 1.130
404     ## ISSUE: Unsupported encoding is not ignored according to the spec.
405 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
406     ($byte_stream, allow_error_reporting => 1,
407 wakaba 1.130 allow_fallback => 1);
408 wakaba 1.133 if ($char_stream) {
409 wakaba 1.130 $self->{confident} = 1;
410     last SNIFFING;
411 wakaba 1.133 } else {
412     ## TODO: unsupported error
413 wakaba 1.130 }
414     }
415    
416     ## Step 2
417 wakaba 1.133 my $byte_buffer = '';
418     for (1..1024) {
419     my $char = $byte_stream->getc;
420     last unless defined $char;
421     $byte_buffer .= $char;
422     } ## TODO: timeout
423 wakaba 1.130
424     ## Step 3
425 wakaba 1.133 if ($byte_buffer =~ /^\xFE\xFF/) {
426 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
427 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
428     ($byte_stream, allow_error_reporting => 1,
429     allow_fallback => 1, byte_buffer => \$byte_buffer);
430 wakaba 1.130 $self->{confident} = 1;
431     last SNIFFING;
432 wakaba 1.133 } elsif ($byte_buffer =~ /^\xFF\xFE/) {
433 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
434 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
435     ($byte_stream, allow_error_reporting => 1,
436     allow_fallback => 1, byte_buffer => \$byte_buffer);
437 wakaba 1.130 $self->{confident} = 1;
438     last SNIFFING;
439 wakaba 1.133 } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
440 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-8');
441 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
442     ($byte_stream, allow_error_reporting => 1,
443     allow_fallback => 1, byte_buffer => \$byte_buffer);
444 wakaba 1.130 $self->{confident} = 1;
445     last SNIFFING;
446     }
447    
448     ## Step 4
449     ## TODO: <meta charset>
450    
451     ## Step 5
452     ## TODO: from history
453    
454     ## Step 6
455 wakaba 1.67 require Whatpm::Charset::UniversalCharDet;
456 wakaba 1.130 $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
457 wakaba 1.133 ($byte_buffer);
458 wakaba 1.130 if (defined $charset_name) {
459 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
460 wakaba 1.130
461     ## ISSUE: Unsupported encoding is not ignored according to the spec.
462 wakaba 1.133 require Whatpm::Charset::DecodeHandle;
463     $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
464     ($byte_stream);
465     ($char_stream, $e_status) = $charset->get_decode_handle
466     ($buffer, allow_error_reporting => 1,
467     allow_fallback => 1, byte_buffer => \$byte_buffer);
468     if ($char_stream) {
469     $buffer->{buffer} = $byte_buffer;
470 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'sniffing:chardet',
471     text => $charset_name,
472     level => $self->{level}->{info},
473     layer => 'encode',
474 wakaba 1.131 line => 1, column => 1);
475 wakaba 1.130 $self->{confident} = 0;
476     last SNIFFING;
477     }
478     }
479    
480     ## Step 7: default
481     ## TODO: Make this configurable.
482 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
483 wakaba 1.130 ## NOTE: We choose |windows-1252| here, since |utf-8| should be
484     ## detectable in the step 6.
485 wakaba 1.133 require Whatpm::Charset::DecodeHandle;
486     $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
487     ($byte_stream);
488     ($char_stream, $e_status)
489     = $charset->get_decode_handle ($buffer,
490     allow_error_reporting => 1,
491     allow_fallback => 1,
492     byte_buffer => \$byte_buffer);
493     $buffer->{buffer} = $byte_buffer;
494 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'sniffing:default',
495     text => 'windows-1252',
496     level => $self->{level}->{info},
497     line => 1, column => 1,
498     layer => 'encode');
499 wakaba 1.65 $self->{confident} = 0;
500 wakaba 1.130 } # SNIFFING
501    
502     if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
503 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
504 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:fallback',
505 wakaba 1.156 #text => $self->{input_encoding},
506 wakaba 1.150 level => $self->{level}->{uncertain},
507     line => 1, column => 1,
508     layer => 'encode');
509 wakaba 1.130 } elsif (not ($e_status &
510 wakaba 1.174 Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
511 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name;
512 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:no error',
513     text => $self->{input_encoding},
514     level => $self->{level}->{uncertain},
515     line => 1, column => 1,
516     layer => 'encode');
517 wakaba 1.156 } else {
518     $self->{input_encoding} = $charset->get_iana_name;
519 wakaba 1.65 }
520    
521     $self->{change_encoding} = sub {
522     my $self = shift;
523 wakaba 1.131 $charset_name = shift;
524 wakaba 1.112 my $token = shift;
525 wakaba 1.65
526 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
527 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
528     ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
529     byte_buffer => \ $buffer->{buffer});
530 wakaba 1.131
531 wakaba 1.133 if ($char_stream) { # if supported
532 wakaba 1.131 ## "Change the encoding" algorithm:
533    
534     ## Step 1
535 wakaba 1.146 if ($charset->{category} &
536     Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
537 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-8');
538 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
539     ($byte_stream,
540     byte_buffer => \ $buffer->{buffer});
541 wakaba 1.131 }
542     $charset_name = $charset->get_iana_name;
543    
544     ## Step 2
545     if (defined $self->{input_encoding} and
546     $self->{input_encoding} eq $charset_name) {
547 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'charset label:matching',
548     text => $charset_name,
549     level => $self->{level}->{info});
550 wakaba 1.131 $self->{confident} = 1;
551     return;
552     }
553 wakaba 1.65
554 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'charset label detected',
555     text => $self->{input_encoding},
556     value => $charset_name,
557     level => $self->{level}->{warn},
558     token => $token);
559 wakaba 1.131
560     ## Step 3
561     # if (can) {
562     ## change the encoding on the fly.
563     #$self->{confident} = 1;
564     #return;
565     # }
566    
567     ## Step 4
568     throw Whatpm::HTML::RestartParser ();
569 wakaba 1.65 }
570     }; # $self->{change_encoding}
571    
572 wakaba 1.133 my $char_onerror = sub {
573     my (undef, $type, %opt) = @_;
574 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, layer => 'encode',
575 wakaba 1.170 line => $self->{line}, column => $self->{column} + 1,
576     %opt, type => $type);
577 wakaba 1.133 if ($opt{octets}) {
578     ${$opt{octets}} = "\x{FFFD}"; # relacement character
579     }
580     };
581 wakaba 1.158
582     my $wrapped_char_stream = $get_wrapper->($char_stream);
583     $wrapped_char_stream->onerror ($char_onerror);
584 wakaba 1.133
585 wakaba 1.178 my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
586 wakaba 1.65 my $return;
587     try {
588 wakaba 1.158 $return = $self->parse_char_stream ($wrapped_char_stream, @args);
589 wakaba 1.65 } catch Whatpm::HTML::RestartParser with {
590 wakaba 1.131 ## NOTE: Invoked after {change_encoding}.
591    
592     if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
593 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
594 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:fallback',
595     level => $self->{level}->{uncertain},
596 wakaba 1.156 #text => $self->{input_encoding},
597 wakaba 1.150 line => 1, column => 1,
598     layer => 'encode');
599 wakaba 1.131 } elsif (not ($e_status &
600 wakaba 1.174 Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
601 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name;
602 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:no error',
603     text => $self->{input_encoding},
604     level => $self->{level}->{uncertain},
605     line => 1, column => 1,
606     layer => 'encode');
607 wakaba 1.156 } else {
608     $self->{input_encoding} = $charset->get_iana_name;
609 wakaba 1.131 }
610 wakaba 1.65 $self->{confident} = 1;
611 wakaba 1.158
612     $wrapped_char_stream = $get_wrapper->($char_stream);
613     $wrapped_char_stream->onerror ($char_onerror);
614    
615     $return = $self->parse_char_stream ($wrapped_char_stream, @args);
616 wakaba 1.65 };
617     return $return;
618 wakaba 1.135 } # parse_byte_stream
619 wakaba 1.65
620 wakaba 1.71 ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
621     ## and the HTML layer MUST ignore it. However, we does strip BOM in
622     ## the encoding layer and the HTML layer does not ignore any U+FEFF,
623     ## because the core part of our HTML parser expects a string of character,
624     ## not a string of bytes or code units or anything which might contain a BOM.
625     ## Therefore, any parser interface that accepts a string of bytes,
626     ## such as |parse_byte_string| in this module, must ensure that it does
627     ## strip the BOM and never strip any ZWNBSP.
628    
629 wakaba 1.158 sub parse_char_string ($$$;$$) {
630     #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
631 wakaba 1.132 my $self = shift;
632 wakaba 1.136 my $s = ref $_[0] ? $_[0] : \($_[0]);
633 wakaba 1.167 require Whatpm::Charset::DecodeHandle;
634     my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
635 wakaba 1.132 return $self->parse_char_stream ($input, @_[1..$#_]);
636     } # parse_char_string
637 wakaba 1.158 *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
638 wakaba 1.65
639 wakaba 1.178 sub parse_char_stream ($$$;$$) {
640 wakaba 1.65 my $self = ref $_[0] ? shift : shift->new;
641 wakaba 1.132 my $input = $_[0];
642 wakaba 1.1 $self->{document} = $_[1];
643 wakaba 1.65 @{$self->{document}->child_nodes} = ();
644 wakaba 1.1
645 wakaba 1.3 ## NOTE: |set_inner_html| copies most of this method's code
646    
647 wakaba 1.65 $self->{confident} = 1 unless exists $self->{confident};
648 wakaba 1.66 $self->{document}->input_encoding ($self->{input_encoding})
649     if defined $self->{input_encoding};
650 wakaba 1.174 ## TODO: |{input_encoding}| is needless?
651 wakaba 1.65
652 wakaba 1.110 $self->{line_prev} = $self->{line} = 1;
653 wakaba 1.175 $self->{column_prev} = -1;
654     $self->{column} = 0;
655 wakaba 1.179 $self->{set_nc} = sub {
656 wakaba 1.1 my $self = shift;
657 wakaba 1.13
658 wakaba 1.174 my $char = '';
659 wakaba 1.179 if (defined $self->{next_nc}) {
660     $char = $self->{next_nc};
661     delete $self->{next_nc};
662     $self->{nc} = ord $char;
663 wakaba 1.136 } else {
664 wakaba 1.175 $self->{char_buffer} = '';
665     $self->{char_buffer_pos} = 0;
666    
667     my $count = $input->manakai_read_until
668 wakaba 1.178 ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
669 wakaba 1.175 if ($count) {
670     $self->{line_prev} = $self->{line};
671     $self->{column_prev} = $self->{column};
672     $self->{column}++;
673 wakaba 1.179 $self->{nc}
674 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
675     return;
676     }
677    
678 wakaba 1.174 if ($input->read ($char, 1)) {
679 wakaba 1.179 $self->{nc} = ord $char;
680 wakaba 1.174 } else {
681 wakaba 1.179 $self->{nc} = -1;
682 wakaba 1.174 return;
683     }
684 wakaba 1.136 }
685 wakaba 1.110
686     ($self->{line_prev}, $self->{column_prev})
687     = ($self->{line}, $self->{column});
688     $self->{column}++;
689 wakaba 1.1
690 wakaba 1.179 if ($self->{nc} == 0x000A) { # LF
691 wakaba 1.129
692 wakaba 1.110 $self->{line}++;
693     $self->{column} = 0;
694 wakaba 1.179 } elsif ($self->{nc} == 0x000D) { # CR
695 wakaba 1.129
696 wakaba 1.166 ## TODO: support for abort/streaming
697 wakaba 1.174 my $next = '';
698     if ($input->read ($next, 1) and $next ne "\x0A") {
699 wakaba 1.179 $self->{next_nc} = $next;
700 wakaba 1.132 }
701 wakaba 1.179 $self->{nc} = 0x000A; # LF # MUST
702 wakaba 1.110 $self->{line}++;
703     $self->{column} = 0;
704 wakaba 1.179 } elsif ($self->{nc} == 0x0000) { # NULL
705 wakaba 1.129
706 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'NULL');
707 wakaba 1.179 $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
708 wakaba 1.1 }
709     };
710    
711 wakaba 1.168 $self->{read_until} = sub {
712     #my ($scalar, $specials_range, $offset) = @_;
713 wakaba 1.179 return 0 if defined $self->{next_nc};
714 wakaba 1.176
715 wakaba 1.178 my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
716 wakaba 1.176 my $offset = $_[2] || 0;
717    
718     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
719     pos ($self->{char_buffer}) = $self->{char_buffer_pos};
720     if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
721     substr ($_[0], $offset)
722     = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
723     my $count = $+[0] - $-[0];
724     if ($count) {
725     $self->{column} += $count;
726     $self->{char_buffer_pos} += $count;
727     $self->{line_prev} = $self->{line};
728     $self->{column_prev} = $self->{column} - 1;
729 wakaba 1.179 $self->{nc} = -1;
730 wakaba 1.176 }
731     return $count;
732     } else {
733     return 0;
734     }
735     } else {
736     my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
737     if ($count) {
738     $self->{column} += $count;
739     $self->{line_prev} = $self->{line};
740     $self->{column_prev} = $self->{column} - 1;
741 wakaba 1.179 $self->{nc} = -1;
742 wakaba 1.176 }
743     return $count;
744 wakaba 1.168 }
745     }; # $self->{read_until}
746 wakaba 1.167
747 wakaba 1.3 my $onerror = $_[2] || sub {
748     my (%opt) = @_;
749 wakaba 1.110 my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
750     my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
751     warn "Parse error ($opt{type}) at line $line column $column\n";
752 wakaba 1.3 };
753     $self->{parse_error} = sub {
754 wakaba 1.110 $onerror->(line => $self->{line}, column => $self->{column}, @_);
755 wakaba 1.1 };
756    
757 wakaba 1.178 my $char_onerror = sub {
758     my (undef, $type, %opt) = @_;
759     $self->{parse_error}->(level => $self->{level}->{must}, layer => 'encode',
760     line => $self->{line}, column => $self->{column} + 1,
761     %opt, type => $type);
762     }; # $char_onerror
763    
764     if ($_[3]) {
765     $input = $_[3]->($input);
766     $input->onerror ($char_onerror);
767     } else {
768     $input->onerror ($char_onerror) unless defined $input->onerror;
769     }
770    
771 wakaba 1.1 $self->_initialize_tokenizer;
772     $self->_initialize_tree_constructor;
773     $self->_construct_tree;
774     $self->_terminate_tree_constructor;
775    
776 wakaba 1.110 delete $self->{parse_error}; # remove loop
777    
778 wakaba 1.1 return $self->{document};
779 wakaba 1.132 } # parse_char_stream
780 wakaba 1.1
781     sub new ($) {
782     my $class = shift;
783 wakaba 1.131 my $self = bless {
784 wakaba 1.150 level => {must => 'm',
785 wakaba 1.155 should => 's',
786 wakaba 1.150 warn => 'w',
787     info => 'i',
788     uncertain => 'u'},
789 wakaba 1.131 }, $class;
790 wakaba 1.179 $self->{set_nc} = sub {
791     $self->{nc} = -1;
792 wakaba 1.1 };
793     $self->{parse_error} = sub {
794     #
795     };
796 wakaba 1.65 $self->{change_encoding} = sub {
797     # if ($_[0] is a supported encoding) {
798     # run "change the encoding" algorithm;
799     # throw Whatpm::HTML::RestartParser (charset => $new_encoding);
800     # }
801     };
802 wakaba 1.63 $self->{application_cache_selection} = sub {
803     #
804     };
805 wakaba 1.1 return $self;
806     } # new
807    
808 wakaba 1.41 sub CM_ENTITY () { 0b001 } # & markup in data
809     sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
810     sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
811    
812     sub PLAINTEXT_CONTENT_MODEL () { 0 }
813     sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
814     sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
815     sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
816    
817 wakaba 1.59 sub DATA_STATE () { 0 }
818 wakaba 1.164 #sub ENTITY_DATA_STATE () { 1 }
819 wakaba 1.59 sub TAG_OPEN_STATE () { 2 }
820     sub CLOSE_TAG_OPEN_STATE () { 3 }
821     sub TAG_NAME_STATE () { 4 }
822     sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
823     sub ATTRIBUTE_NAME_STATE () { 6 }
824     sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
825     sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
826     sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
827     sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
828     sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
829 wakaba 1.164 #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
830 wakaba 1.59 sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
831     sub COMMENT_START_STATE () { 14 }
832     sub COMMENT_START_DASH_STATE () { 15 }
833     sub COMMENT_STATE () { 16 }
834     sub COMMENT_END_STATE () { 17 }
835     sub COMMENT_END_DASH_STATE () { 18 }
836     sub BOGUS_COMMENT_STATE () { 19 }
837     sub DOCTYPE_STATE () { 20 }
838     sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
839     sub DOCTYPE_NAME_STATE () { 22 }
840     sub AFTER_DOCTYPE_NAME_STATE () { 23 }
841     sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
842     sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
843     sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
844     sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
845     sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
846     sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
847     sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
848     sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
849     sub BOGUS_DOCTYPE_STATE () { 32 }
850 wakaba 1.72 sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
851 wakaba 1.122 sub SELF_CLOSING_START_TAG_STATE () { 34 }
852 wakaba 1.161 sub CDATA_SECTION_STATE () { 35 }
853 wakaba 1.160 sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
854     sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
855     sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
856     sub CDATA_PCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
857 wakaba 1.161 sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
858     sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
859 wakaba 1.162 sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
860     sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
861 wakaba 1.165 ## NOTE: "Entity data state", "entity in attribute value state", and
862     ## "consume a character reference" algorithm are jointly implemented
863     ## using the following six states:
864     sub ENTITY_STATE () { 44 }
865     sub ENTITY_HASH_STATE () { 45 }
866     sub NCR_NUM_STATE () { 46 }
867     sub HEXREF_X_STATE () { 47 }
868     sub HEXREF_HEX_STATE () { 48 }
869     sub ENTITY_NAME_STATE () { 49 }
870 wakaba 1.59
871 wakaba 1.57 sub DOCTYPE_TOKEN () { 1 }
872     sub COMMENT_TOKEN () { 2 }
873     sub START_TAG_TOKEN () { 3 }
874     sub END_TAG_TOKEN () { 4 }
875     sub END_OF_FILE_TOKEN () { 5 }
876     sub CHARACTER_TOKEN () { 6 }
877    
878 wakaba 1.56 sub AFTER_HTML_IMS () { 0b100 }
879     sub HEAD_IMS () { 0b1000 }
880     sub BODY_IMS () { 0b10000 }
881 wakaba 1.58 sub BODY_TABLE_IMS () { 0b100000 }
882 wakaba 1.56 sub TABLE_IMS () { 0b1000000 }
883 wakaba 1.58 sub ROW_IMS () { 0b10000000 }
884 wakaba 1.56 sub BODY_AFTER_IMS () { 0b100000000 }
885     sub FRAME_IMS () { 0b1000000000 }
886 wakaba 1.101 sub SELECT_IMS () { 0b10000000000 }
887 wakaba 1.123 sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
888     ## NOTE: "in foreign content" insertion mode is special; it is combined
889     ## with the secondary insertion mode. In this parser, they are stored
890     ## together in the bit-or'ed form.
891 wakaba 1.56
892 wakaba 1.85 ## NOTE: "initial" and "before html" insertion modes have no constants.
893 wakaba 1.84
894     ## NOTE: "after after body" insertion mode.
895 wakaba 1.56 sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
896 wakaba 1.84
897     ## NOTE: "after after frameset" insertion mode.
898 wakaba 1.56 sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
899 wakaba 1.84
900 wakaba 1.56 sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
901     sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
902     sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
903     sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
904     sub IN_BODY_IM () { BODY_IMS }
905 wakaba 1.58 sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
906     sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
907     sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
908     sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
909 wakaba 1.56 sub IN_TABLE_IM () { TABLE_IMS }
910     sub AFTER_BODY_IM () { BODY_AFTER_IMS }
911     sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
912     sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
913 wakaba 1.101 sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
914     sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
915 wakaba 1.56 sub IN_COLUMN_GROUP_IM () { 0b10 }
916    
917 wakaba 1.1 ## Implementations MUST act as if state machine in the spec
918    
919     sub _initialize_tokenizer ($) {
920     my $self = shift;
921 wakaba 1.59 $self->{state} = DATA_STATE; # MUST
922 wakaba 1.179 #$self->{s_kwd}; # state keyword - initialized when used
923 wakaba 1.165 #$self->{entity__value}; # initialized when used
924     #$self->{entity__match}; # initialized when used
925 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # be
926 wakaba 1.179 undef $self->{ct}; # current token
927     undef $self->{ca}; # current attribute
928     undef $self->{last_stag_name}; # last emitted start tag name
929 wakaba 1.165 #$self->{prev_state}; # initialized when used
930 wakaba 1.122 delete $self->{self_closing};
931 wakaba 1.175 $self->{char_buffer} = '';
932     $self->{char_buffer_pos} = 0;
933 wakaba 1.179 $self->{nc} = -1; # next input character
934     #$self->{next_nc}
935 wakaba 1.1
936 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
937     $self->{line_prev} = $self->{line};
938     $self->{column_prev} = $self->{column};
939     $self->{column}++;
940 wakaba 1.179 $self->{nc}
941 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
942     } else {
943 wakaba 1.179 $self->{set_nc}->($self);
944 wakaba 1.175 }
945 wakaba 1.1
946     $self->{token} = [];
947 wakaba 1.18 # $self->{escape}
948 wakaba 1.1 } # _initialize_tokenizer
949    
950     ## A token has:
951 wakaba 1.57 ## ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
952     ## CHARACTER_TOKEN, or END_OF_FILE_TOKEN
953     ## ->{name} (DOCTYPE_TOKEN)
954     ## ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
955 wakaba 1.179 ## ->{pubid} (DOCTYPE_TOKEN)
956     ## ->{sysid} (DOCTYPE_TOKEN)
957 wakaba 1.75 ## ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
958 wakaba 1.57 ## ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
959 wakaba 1.68 ## ->{name}
960     ## ->{value}
961     ## ->{has_reference} == 1 or 0
962 wakaba 1.57 ## ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
963 wakaba 1.122 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
964     ## |->{self_closing}| is used to save the value of |$self->{self_closing}|
965     ## while the token is pushed back to the stack.
966    
967 wakaba 1.1 ## Emitted token MUST immediately be handled by the tree construction state.
968    
969     ## Before each step, UA MAY check to see if either one of the scripts in
970     ## "list of scripts that will execute as soon as possible" or the first
971     ## script in the "list of scripts that will execute asynchronously",
972     ## has completed loading. If one has, then it MUST be executed
973     ## and removed from the list.
974    
975 wakaba 1.165 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
976     ## (This requirement was dropped from HTML5 spec, unfortunately.)
977 wakaba 1.61
978 wakaba 1.1 sub _get_next_token ($) {
979     my $self = shift;
980 wakaba 1.122
981     if ($self->{self_closing}) {
982 wakaba 1.179 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc', token => $self->{ct});
983 wakaba 1.122 ## NOTE: The |self_closing| flag is only set by start tag token.
984     ## In addition, when a start tag token is emitted, it is always set to
985 wakaba 1.179 ## |ct|.
986 wakaba 1.122 delete $self->{self_closing};
987     }
988    
989 wakaba 1.1 if (@{$self->{token}}) {
990 wakaba 1.122 $self->{self_closing} = $self->{token}->[0]->{self_closing};
991 wakaba 1.1 return shift @{$self->{token}};
992     }
993    
994     A: {
995 wakaba 1.59 if ($self->{state} == DATA_STATE) {
996 wakaba 1.179 if ($self->{nc} == 0x0026) { # &
997 wakaba 1.180 delete $self->{s_kwd};
998 wakaba 1.72 if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
999     not $self->{escape}) {
1000 wakaba 1.77
1001 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
1002     ## "entity data state". In this implementation, the tokenizer
1003     ## is switched to the |ENTITY_STATE|, which is an implementation
1004     ## of the "consume a character reference" algorithm.
1005 wakaba 1.179 $self->{entity_add} = -1;
1006 wakaba 1.165 $self->{prev_state} = DATA_STATE;
1007 wakaba 1.163 $self->{state} = ENTITY_STATE;
1008 wakaba 1.1
1009 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1010     $self->{line_prev} = $self->{line};
1011     $self->{column_prev} = $self->{column};
1012     $self->{column}++;
1013 wakaba 1.179 $self->{nc}
1014 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1015     } else {
1016 wakaba 1.179 $self->{set_nc}->($self);
1017 wakaba 1.175 }
1018 wakaba 1.1
1019     redo A;
1020     } else {
1021 wakaba 1.77
1022 wakaba 1.1 #
1023     }
1024 wakaba 1.179 } elsif ($self->{nc} == 0x002D) { # -
1025 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1026 wakaba 1.180 if (defined $self->{s_kwd}) {
1027    
1028     $self->{s_kwd} .= '-';
1029     } else {
1030    
1031     $self->{s_kwd} = '-';
1032     }
1033    
1034     if ($self->{s_kwd} eq '<!--') {
1035    
1036     $self->{escape} = 1; # unless $self->{escape};
1037     $self->{s_kwd} = '--';
1038     #
1039     } elsif ($self->{s_kwd} eq '---') {
1040    
1041     $self->{s_kwd} = '--';
1042     #
1043 wakaba 1.77 } else {
1044    
1045 wakaba 1.180 #
1046 wakaba 1.13 }
1047     }
1048    
1049     #
1050 wakaba 1.180 } elsif ($self->{nc} == 0x0021) { # !
1051     if (defined $self->{s_kwd}) {
1052    
1053     $self->{s_kwd} .= '!';
1054     #
1055     } else {
1056    
1057     #
1058     }
1059     #
1060 wakaba 1.179 } elsif ($self->{nc} == 0x003C) { # <
1061 wakaba 1.180 delete $self->{s_kwd};
1062 wakaba 1.41 if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1063     (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1064 wakaba 1.13 not $self->{escape})) {
1065 wakaba 1.77
1066 wakaba 1.59 $self->{state} = TAG_OPEN_STATE;
1067 wakaba 1.1
1068 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1069     $self->{line_prev} = $self->{line};
1070     $self->{column_prev} = $self->{column};
1071     $self->{column}++;
1072 wakaba 1.179 $self->{nc}
1073 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1074     } else {
1075 wakaba 1.179 $self->{set_nc}->($self);
1076 wakaba 1.175 }
1077 wakaba 1.1
1078     redo A;
1079     } else {
1080 wakaba 1.77
1081 wakaba 1.1 #
1082     }
1083 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1084 wakaba 1.13 if ($self->{escape} and
1085 wakaba 1.41 ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1086 wakaba 1.180 if (defined $self->{s_kwd} and $self->{s_kwd} eq '--') {
1087 wakaba 1.77
1088 wakaba 1.13 delete $self->{escape};
1089 wakaba 1.77 } else {
1090    
1091 wakaba 1.13 }
1092 wakaba 1.77 } else {
1093    
1094 wakaba 1.13 }
1095    
1096 wakaba 1.180 delete $self->{s_kwd};
1097 wakaba 1.13 #
1098 wakaba 1.179 } elsif ($self->{nc} == -1) {
1099 wakaba 1.77
1100 wakaba 1.180 delete $self->{s_kwd};
1101 wakaba 1.110 return ({type => END_OF_FILE_TOKEN,
1102     line => $self->{line}, column => $self->{column}});
1103 wakaba 1.1 last A; ## TODO: ok?
1104 wakaba 1.77 } else {
1105    
1106 wakaba 1.180 delete $self->{s_kwd};
1107     #
1108 wakaba 1.1 }
1109 wakaba 1.180
1110 wakaba 1.1 # Anything else
1111 wakaba 1.57 my $token = {type => CHARACTER_TOKEN,
1112 wakaba 1.179 data => chr $self->{nc},
1113 wakaba 1.118 line => $self->{line}, column => $self->{column},
1114 wakaba 1.116 };
1115 wakaba 1.180 if ($self->{read_until}->($token->{data}, q[-!<>&],
1116     length $token->{data})) {
1117     delete $self->{s_kwd};
1118     }
1119 wakaba 1.167
1120 wakaba 1.1 ## Stay in the data state
1121    
1122 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1123     $self->{line_prev} = $self->{line};
1124     $self->{column_prev} = $self->{column};
1125     $self->{column}++;
1126 wakaba 1.179 $self->{nc}
1127 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1128     } else {
1129 wakaba 1.179 $self->{set_nc}->($self);
1130 wakaba 1.175 }
1131 wakaba 1.1
1132    
1133     return ($token);
1134    
1135     redo A;
1136 wakaba 1.59 } elsif ($self->{state} == TAG_OPEN_STATE) {
1137 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1138 wakaba 1.179 if ($self->{nc} == 0x002F) { # /
1139 wakaba 1.1
1140 wakaba 1.77
1141 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1142     $self->{line_prev} = $self->{line};
1143     $self->{column_prev} = $self->{column};
1144     $self->{column}++;
1145 wakaba 1.179 $self->{nc}
1146 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1147     } else {
1148 wakaba 1.179 $self->{set_nc}->($self);
1149 wakaba 1.175 }
1150 wakaba 1.1
1151 wakaba 1.59 $self->{state} = CLOSE_TAG_OPEN_STATE;
1152 wakaba 1.1 redo A;
1153 wakaba 1.180 } elsif ($self->{nc} == 0x0021) { # !
1154    
1155     $self->{s_kwd} = '<' unless $self->{escape};
1156     #
1157 wakaba 1.1 } else {
1158 wakaba 1.77
1159 wakaba 1.180 #
1160     }
1161 wakaba 1.1
1162 wakaba 1.180 ## reconsume
1163     $self->{state} = DATA_STATE;
1164     return ({type => CHARACTER_TOKEN, data => '<',
1165     line => $self->{line_prev},
1166     column => $self->{column_prev},
1167     });
1168     redo A;
1169 wakaba 1.41 } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1170 wakaba 1.179 if ($self->{nc} == 0x0021) { # !
1171 wakaba 1.77
1172 wakaba 1.59 $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1173 wakaba 1.1
1174 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1175     $self->{line_prev} = $self->{line};
1176     $self->{column_prev} = $self->{column};
1177     $self->{column}++;
1178 wakaba 1.179 $self->{nc}
1179 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1180     } else {
1181 wakaba 1.179 $self->{set_nc}->($self);
1182 wakaba 1.175 }
1183 wakaba 1.1
1184     redo A;
1185 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1186 wakaba 1.77
1187 wakaba 1.59 $self->{state} = CLOSE_TAG_OPEN_STATE;
1188 wakaba 1.1
1189 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1190     $self->{line_prev} = $self->{line};
1191     $self->{column_prev} = $self->{column};
1192     $self->{column}++;
1193 wakaba 1.179 $self->{nc}
1194 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1195     } else {
1196 wakaba 1.179 $self->{set_nc}->($self);
1197 wakaba 1.175 }
1198 wakaba 1.1
1199     redo A;
1200 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1201     $self->{nc} <= 0x005A) { # A..Z
1202 wakaba 1.77
1203 wakaba 1.179 $self->{ct}
1204 wakaba 1.57 = {type => START_TAG_TOKEN,
1205 wakaba 1.179 tag_name => chr ($self->{nc} + 0x0020),
1206 wakaba 1.110 line => $self->{line_prev},
1207     column => $self->{column_prev}};
1208 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1209 wakaba 1.1
1210 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1211     $self->{line_prev} = $self->{line};
1212     $self->{column_prev} = $self->{column};
1213     $self->{column}++;
1214 wakaba 1.179 $self->{nc}
1215 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1216     } else {
1217 wakaba 1.179 $self->{set_nc}->($self);
1218 wakaba 1.175 }
1219 wakaba 1.1
1220     redo A;
1221 wakaba 1.179 } elsif (0x0061 <= $self->{nc} and
1222     $self->{nc} <= 0x007A) { # a..z
1223 wakaba 1.77
1224 wakaba 1.179 $self->{ct} = {type => START_TAG_TOKEN,
1225     tag_name => chr ($self->{nc}),
1226 wakaba 1.110 line => $self->{line_prev},
1227     column => $self->{column_prev}};
1228 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1229 wakaba 1.1
1230 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1231     $self->{line_prev} = $self->{line};
1232     $self->{column_prev} = $self->{column};
1233     $self->{column}++;
1234 wakaba 1.179 $self->{nc}
1235 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1236     } else {
1237 wakaba 1.179 $self->{set_nc}->($self);
1238 wakaba 1.175 }
1239 wakaba 1.1
1240     redo A;
1241 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1242 wakaba 1.77
1243 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty start tag',
1244 wakaba 1.113 line => $self->{line_prev},
1245     column => $self->{column_prev});
1246 wakaba 1.59 $self->{state} = DATA_STATE;
1247 wakaba 1.1
1248 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1249     $self->{line_prev} = $self->{line};
1250     $self->{column_prev} = $self->{column};
1251     $self->{column}++;
1252 wakaba 1.179 $self->{nc}
1253 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1254     } else {
1255 wakaba 1.179 $self->{set_nc}->($self);
1256 wakaba 1.175 }
1257 wakaba 1.1
1258    
1259 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '<>',
1260 wakaba 1.118 line => $self->{line_prev},
1261     column => $self->{column_prev},
1262 wakaba 1.116 });
1263 wakaba 1.1
1264     redo A;
1265 wakaba 1.179 } elsif ($self->{nc} == 0x003F) { # ?
1266 wakaba 1.77
1267 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'pio',
1268 wakaba 1.113 line => $self->{line_prev},
1269     column => $self->{column_prev});
1270 wakaba 1.59 $self->{state} = BOGUS_COMMENT_STATE;
1271 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN, data => '',
1272 wakaba 1.118 line => $self->{line_prev},
1273     column => $self->{column_prev},
1274 wakaba 1.116 };
1275 wakaba 1.179 ## $self->{nc} is intentionally left as is
1276 wakaba 1.1 redo A;
1277     } else {
1278 wakaba 1.77
1279 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare stago',
1280 wakaba 1.133 line => $self->{line_prev},
1281     column => $self->{column_prev});
1282 wakaba 1.59 $self->{state} = DATA_STATE;
1283 wakaba 1.1 ## reconsume
1284    
1285 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '<',
1286 wakaba 1.118 line => $self->{line_prev},
1287     column => $self->{column_prev},
1288 wakaba 1.116 });
1289 wakaba 1.1
1290     redo A;
1291     }
1292     } else {
1293 wakaba 1.41 die "$0: $self->{content_model} in tag open";
1294 wakaba 1.1 }
1295 wakaba 1.59 } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1296 wakaba 1.160 ## NOTE: The "close tag open state" in the spec is implemented as
1297     ## |CLOSE_TAG_OPEN_STATE| and |CDATA_PCDATA_CLOSE_TAG_STATE|.
1298    
1299 wakaba 1.112 my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1300 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1301 wakaba 1.179 if (defined $self->{last_stag_name}) {
1302 wakaba 1.160 $self->{state} = CDATA_PCDATA_CLOSE_TAG_STATE;
1303 wakaba 1.179 $self->{s_kwd} = '';
1304 wakaba 1.160 ## Reconsume.
1305     redo A;
1306 wakaba 1.23 } else {
1307     ## No start tag token has ever been emitted
1308 wakaba 1.161 ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1309 wakaba 1.77
1310 wakaba 1.59 $self->{state} = DATA_STATE;
1311 wakaba 1.161 ## Reconsume.
1312 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '</',
1313 wakaba 1.118 line => $l, column => $c,
1314 wakaba 1.116 });
1315 wakaba 1.1 redo A;
1316     }
1317     }
1318 wakaba 1.160
1319 wakaba 1.179 if (0x0041 <= $self->{nc} and
1320     $self->{nc} <= 0x005A) { # A..Z
1321 wakaba 1.77
1322 wakaba 1.179 $self->{ct}
1323 wakaba 1.110 = {type => END_TAG_TOKEN,
1324 wakaba 1.179 tag_name => chr ($self->{nc} + 0x0020),
1325 wakaba 1.110 line => $l, column => $c};
1326 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1327 wakaba 1.1
1328 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1329     $self->{line_prev} = $self->{line};
1330     $self->{column_prev} = $self->{column};
1331     $self->{column}++;
1332 wakaba 1.179 $self->{nc}
1333 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1334     } else {
1335 wakaba 1.179 $self->{set_nc}->($self);
1336 wakaba 1.175 }
1337 wakaba 1.1
1338     redo A;
1339 wakaba 1.179 } elsif (0x0061 <= $self->{nc} and
1340     $self->{nc} <= 0x007A) { # a..z
1341 wakaba 1.77
1342 wakaba 1.179 $self->{ct} = {type => END_TAG_TOKEN,
1343     tag_name => chr ($self->{nc}),
1344 wakaba 1.110 line => $l, column => $c};
1345 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1346 wakaba 1.1
1347 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1348     $self->{line_prev} = $self->{line};
1349     $self->{column_prev} = $self->{column};
1350     $self->{column}++;
1351 wakaba 1.179 $self->{nc}
1352 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1353     } else {
1354 wakaba 1.179 $self->{set_nc}->($self);
1355 wakaba 1.175 }
1356 wakaba 1.1
1357     redo A;
1358 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1359 wakaba 1.77
1360 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty end tag',
1361 wakaba 1.113 line => $self->{line_prev}, ## "<" in "</>"
1362     column => $self->{column_prev} - 1);
1363 wakaba 1.59 $self->{state} = DATA_STATE;
1364 wakaba 1.1
1365 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1366     $self->{line_prev} = $self->{line};
1367     $self->{column_prev} = $self->{column};
1368     $self->{column}++;
1369 wakaba 1.179 $self->{nc}
1370 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1371     } else {
1372 wakaba 1.179 $self->{set_nc}->($self);
1373 wakaba 1.175 }
1374 wakaba 1.1
1375     redo A;
1376 wakaba 1.179 } elsif ($self->{nc} == -1) {
1377 wakaba 1.77
1378 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare etago');
1379 wakaba 1.59 $self->{state} = DATA_STATE;
1380 wakaba 1.1 # reconsume
1381    
1382 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '</',
1383 wakaba 1.118 line => $l, column => $c,
1384 wakaba 1.116 });
1385 wakaba 1.1
1386     redo A;
1387     } else {
1388 wakaba 1.77
1389 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus end tag');
1390 wakaba 1.59 $self->{state} = BOGUS_COMMENT_STATE;
1391 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN, data => '',
1392 wakaba 1.118 line => $self->{line_prev}, # "<" of "</"
1393     column => $self->{column_prev} - 1,
1394 wakaba 1.116 };
1395 wakaba 1.179 ## NOTE: $self->{nc} is intentionally left as is.
1396 wakaba 1.160 ## Although the "anything else" case of the spec not explicitly
1397     ## states that the next input character is to be reconsumed,
1398     ## it will be included to the |data| of the comment token
1399     ## generated from the bogus end tag, as defined in the
1400     ## "bogus comment state" entry.
1401     redo A;
1402     }
1403     } elsif ($self->{state} == CDATA_PCDATA_CLOSE_TAG_STATE) {
1404 wakaba 1.179 my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1405 wakaba 1.160 if (length $ch) {
1406     my $CH = $ch;
1407     $ch =~ tr/a-z/A-Z/;
1408 wakaba 1.179 my $nch = chr $self->{nc};
1409 wakaba 1.160 if ($nch eq $ch or $nch eq $CH) {
1410    
1411     ## Stay in the state.
1412 wakaba 1.179 $self->{s_kwd} .= $nch;
1413 wakaba 1.160
1414 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1415     $self->{line_prev} = $self->{line};
1416     $self->{column_prev} = $self->{column};
1417     $self->{column}++;
1418 wakaba 1.179 $self->{nc}
1419 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1420     } else {
1421 wakaba 1.179 $self->{set_nc}->($self);
1422 wakaba 1.175 }
1423 wakaba 1.160
1424     redo A;
1425     } else {
1426    
1427     $self->{state} = DATA_STATE;
1428     ## Reconsume.
1429     return ({type => CHARACTER_TOKEN,
1430 wakaba 1.179 data => '</' . $self->{s_kwd},
1431 wakaba 1.160 line => $self->{line_prev},
1432 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd},
1433 wakaba 1.160 });
1434     redo A;
1435     }
1436     } else { # after "<{tag-name}"
1437     unless ({
1438     0x0009 => 1, # HT
1439     0x000A => 1, # LF
1440     0x000B => 1, # VT
1441     0x000C => 1, # FF
1442     0x0020 => 1, # SP
1443     0x003E => 1, # >
1444     0x002F => 1, # /
1445     -1 => 1, # EOF
1446 wakaba 1.179 }->{$self->{nc}}) {
1447 wakaba 1.160
1448     ## Reconsume.
1449     $self->{state} = DATA_STATE;
1450     return ({type => CHARACTER_TOKEN,
1451 wakaba 1.179 data => '</' . $self->{s_kwd},
1452 wakaba 1.160 line => $self->{line_prev},
1453 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd},
1454 wakaba 1.160 });
1455     redo A;
1456     } else {
1457    
1458 wakaba 1.179 $self->{ct}
1459 wakaba 1.160 = {type => END_TAG_TOKEN,
1460 wakaba 1.179 tag_name => $self->{last_stag_name},
1461 wakaba 1.160 line => $self->{line_prev},
1462 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1463 wakaba 1.160 $self->{state} = TAG_NAME_STATE;
1464     ## Reconsume.
1465     redo A;
1466     }
1467 wakaba 1.1 }
1468 wakaba 1.59 } elsif ($self->{state} == TAG_NAME_STATE) {
1469 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
1470     $self->{nc} == 0x000A or # LF
1471     $self->{nc} == 0x000B or # VT
1472     $self->{nc} == 0x000C or # FF
1473     $self->{nc} == 0x0020) { # SP
1474 wakaba 1.77
1475 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1476 wakaba 1.1
1477 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1478     $self->{line_prev} = $self->{line};
1479     $self->{column_prev} = $self->{column};
1480     $self->{column}++;
1481 wakaba 1.179 $self->{nc}
1482 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1483     } else {
1484 wakaba 1.179 $self->{set_nc}->($self);
1485 wakaba 1.175 }
1486 wakaba 1.1
1487     redo A;
1488 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1489     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1490 wakaba 1.77
1491 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1492     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1493 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1494 wakaba 1.179 #if ($self->{ct}->{attributes}) {
1495 wakaba 1.78 # ## NOTE: This should never be reached.
1496     # !!! cp (36);
1497     # !!! parse-error (type => 'end tag attribute');
1498     #} else {
1499 wakaba 1.77
1500 wakaba 1.78 #}
1501 wakaba 1.1 } else {
1502 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1503 wakaba 1.1 }
1504 wakaba 1.59 $self->{state} = DATA_STATE;
1505 wakaba 1.1
1506 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1507     $self->{line_prev} = $self->{line};
1508     $self->{column_prev} = $self->{column};
1509     $self->{column}++;
1510 wakaba 1.179 $self->{nc}
1511 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1512     } else {
1513 wakaba 1.179 $self->{set_nc}->($self);
1514 wakaba 1.175 }
1515 wakaba 1.1
1516    
1517 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1518 wakaba 1.1
1519     redo A;
1520 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1521     $self->{nc} <= 0x005A) { # A..Z
1522 wakaba 1.77
1523 wakaba 1.179 $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1524 wakaba 1.1 # start tag or end tag
1525     ## Stay in this state
1526    
1527 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1528     $self->{line_prev} = $self->{line};
1529     $self->{column_prev} = $self->{column};
1530     $self->{column}++;
1531 wakaba 1.179 $self->{nc}
1532 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1533     } else {
1534 wakaba 1.179 $self->{set_nc}->($self);
1535 wakaba 1.175 }
1536 wakaba 1.1
1537     redo A;
1538 wakaba 1.179 } elsif ($self->{nc} == -1) {
1539 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1540 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1541 wakaba 1.77
1542 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1543     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1544 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1545 wakaba 1.179 #if ($self->{ct}->{attributes}) {
1546 wakaba 1.78 # ## NOTE: This state should never be reached.
1547     # !!! cp (40);
1548     # !!! parse-error (type => 'end tag attribute');
1549     #} else {
1550 wakaba 1.77
1551 wakaba 1.78 #}
1552 wakaba 1.1 } else {
1553 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1554 wakaba 1.1 }
1555 wakaba 1.59 $self->{state} = DATA_STATE;
1556 wakaba 1.1 # reconsume
1557    
1558 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1559 wakaba 1.1
1560     redo A;
1561 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1562 wakaba 1.1
1563 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1564    
1565 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1566     $self->{line_prev} = $self->{line};
1567     $self->{column_prev} = $self->{column};
1568     $self->{column}++;
1569 wakaba 1.179 $self->{nc}
1570 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1571     } else {
1572 wakaba 1.179 $self->{set_nc}->($self);
1573 wakaba 1.175 }
1574 wakaba 1.1
1575     redo A;
1576     } else {
1577 wakaba 1.77
1578 wakaba 1.179 $self->{ct}->{tag_name} .= chr $self->{nc};
1579 wakaba 1.1 # start tag or end tag
1580     ## Stay in the state
1581    
1582 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1583     $self->{line_prev} = $self->{line};
1584     $self->{column_prev} = $self->{column};
1585     $self->{column}++;
1586 wakaba 1.179 $self->{nc}
1587 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1588     } else {
1589 wakaba 1.179 $self->{set_nc}->($self);
1590 wakaba 1.175 }
1591 wakaba 1.1
1592     redo A;
1593     }
1594 wakaba 1.59 } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1595 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
1596     $self->{nc} == 0x000A or # LF
1597     $self->{nc} == 0x000B or # VT
1598     $self->{nc} == 0x000C or # FF
1599     $self->{nc} == 0x0020) { # SP
1600 wakaba 1.77
1601 wakaba 1.1 ## Stay in the state
1602    
1603 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1604     $self->{line_prev} = $self->{line};
1605     $self->{column_prev} = $self->{column};
1606     $self->{column}++;
1607 wakaba 1.179 $self->{nc}
1608 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1609     } else {
1610 wakaba 1.179 $self->{set_nc}->($self);
1611 wakaba 1.175 }
1612 wakaba 1.1
1613     redo A;
1614 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1615     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1616 wakaba 1.77
1617 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1618     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1619 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1620 wakaba 1.179 if ($self->{ct}->{attributes}) {
1621 wakaba 1.77
1622 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1623 wakaba 1.77 } else {
1624    
1625 wakaba 1.1 }
1626     } else {
1627 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1628 wakaba 1.1 }
1629 wakaba 1.59 $self->{state} = DATA_STATE;
1630 wakaba 1.1
1631 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1632     $self->{line_prev} = $self->{line};
1633     $self->{column_prev} = $self->{column};
1634     $self->{column}++;
1635 wakaba 1.179 $self->{nc}
1636 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1637     } else {
1638 wakaba 1.179 $self->{set_nc}->($self);
1639 wakaba 1.175 }
1640 wakaba 1.1
1641    
1642 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1643 wakaba 1.1
1644     redo A;
1645 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1646     $self->{nc} <= 0x005A) { # A..Z
1647 wakaba 1.77
1648 wakaba 1.179 $self->{ca}
1649     = {name => chr ($self->{nc} + 0x0020),
1650 wakaba 1.117 value => '',
1651     line => $self->{line}, column => $self->{column}};
1652 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
1653 wakaba 1.1
1654 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1655     $self->{line_prev} = $self->{line};
1656     $self->{column_prev} = $self->{column};
1657     $self->{column}++;
1658 wakaba 1.179 $self->{nc}
1659 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1660     } else {
1661 wakaba 1.179 $self->{set_nc}->($self);
1662 wakaba 1.175 }
1663 wakaba 1.1
1664     redo A;
1665 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1666 wakaba 1.1
1667 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1668    
1669 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1670     $self->{line_prev} = $self->{line};
1671     $self->{column_prev} = $self->{column};
1672     $self->{column}++;
1673 wakaba 1.179 $self->{nc}
1674 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1675     } else {
1676 wakaba 1.179 $self->{set_nc}->($self);
1677 wakaba 1.175 }
1678 wakaba 1.1
1679     redo A;
1680 wakaba 1.179 } elsif ($self->{nc} == -1) {
1681 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1682 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1683 wakaba 1.77
1684 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1685     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1686 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1687 wakaba 1.179 if ($self->{ct}->{attributes}) {
1688 wakaba 1.77
1689 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1690 wakaba 1.77 } else {
1691    
1692 wakaba 1.1 }
1693     } else {
1694 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1695 wakaba 1.1 }
1696 wakaba 1.59 $self->{state} = DATA_STATE;
1697 wakaba 1.1 # reconsume
1698    
1699 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1700 wakaba 1.1
1701     redo A;
1702     } else {
1703 wakaba 1.72 if ({
1704     0x0022 => 1, # "
1705     0x0027 => 1, # '
1706     0x003D => 1, # =
1707 wakaba 1.179 }->{$self->{nc}}) {
1708 wakaba 1.77
1709 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
1710 wakaba 1.77 } else {
1711    
1712 wakaba 1.72 }
1713 wakaba 1.179 $self->{ca}
1714     = {name => chr ($self->{nc}),
1715 wakaba 1.117 value => '',
1716     line => $self->{line}, column => $self->{column}};
1717 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
1718 wakaba 1.1
1719 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1720     $self->{line_prev} = $self->{line};
1721     $self->{column_prev} = $self->{column};
1722     $self->{column}++;
1723 wakaba 1.179 $self->{nc}
1724 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1725     } else {
1726 wakaba 1.179 $self->{set_nc}->($self);
1727 wakaba 1.175 }
1728 wakaba 1.1
1729     redo A;
1730     }
1731 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1732 wakaba 1.1 my $before_leave = sub {
1733 wakaba 1.179 if (exists $self->{ct}->{attributes} # start tag or end tag
1734     ->{$self->{ca}->{name}}) { # MUST
1735 wakaba 1.77
1736 wakaba 1.179 $self->{parse_error}->(level => $self->{level}->{must}, type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1737     ## Discard $self->{ca} # MUST
1738 wakaba 1.1 } else {
1739 wakaba 1.77
1740 wakaba 1.179 $self->{ct}->{attributes}->{$self->{ca}->{name}}
1741     = $self->{ca};
1742 wakaba 1.1 }
1743     }; # $before_leave
1744    
1745 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
1746     $self->{nc} == 0x000A or # LF
1747     $self->{nc} == 0x000B or # VT
1748     $self->{nc} == 0x000C or # FF
1749     $self->{nc} == 0x0020) { # SP
1750 wakaba 1.77
1751 wakaba 1.1 $before_leave->();
1752 wakaba 1.59 $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1753 wakaba 1.1
1754 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1755     $self->{line_prev} = $self->{line};
1756     $self->{column_prev} = $self->{column};
1757     $self->{column}++;
1758 wakaba 1.179 $self->{nc}
1759 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1760     } else {
1761 wakaba 1.179 $self->{set_nc}->($self);
1762 wakaba 1.175 }
1763 wakaba 1.1
1764     redo A;
1765 wakaba 1.179 } elsif ($self->{nc} == 0x003D) { # =
1766 wakaba 1.77
1767 wakaba 1.1 $before_leave->();
1768 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1769 wakaba 1.1
1770 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1771     $self->{line_prev} = $self->{line};
1772     $self->{column_prev} = $self->{column};
1773     $self->{column}++;
1774 wakaba 1.179 $self->{nc}
1775 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1776     } else {
1777 wakaba 1.179 $self->{set_nc}->($self);
1778 wakaba 1.175 }
1779 wakaba 1.1
1780     redo A;
1781 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1782 wakaba 1.1 $before_leave->();
1783 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1784 wakaba 1.77
1785 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1786     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1787 wakaba 1.77
1788 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1789 wakaba 1.179 if ($self->{ct}->{attributes}) {
1790 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1791 wakaba 1.1 }
1792     } else {
1793 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1794 wakaba 1.1 }
1795 wakaba 1.59 $self->{state} = DATA_STATE;
1796 wakaba 1.1
1797 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1798     $self->{line_prev} = $self->{line};
1799     $self->{column_prev} = $self->{column};
1800     $self->{column}++;
1801 wakaba 1.179 $self->{nc}
1802 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1803     } else {
1804 wakaba 1.179 $self->{set_nc}->($self);
1805 wakaba 1.175 }
1806 wakaba 1.1
1807    
1808 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1809 wakaba 1.1
1810     redo A;
1811 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1812     $self->{nc} <= 0x005A) { # A..Z
1813 wakaba 1.77
1814 wakaba 1.179 $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1815 wakaba 1.1 ## Stay in the state
1816    
1817 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1818     $self->{line_prev} = $self->{line};
1819     $self->{column_prev} = $self->{column};
1820     $self->{column}++;
1821 wakaba 1.179 $self->{nc}
1822 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1823     } else {
1824 wakaba 1.179 $self->{set_nc}->($self);
1825 wakaba 1.175 }
1826 wakaba 1.1
1827     redo A;
1828 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1829 wakaba 1.122
1830 wakaba 1.1 $before_leave->();
1831 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1832 wakaba 1.1
1833 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1834     $self->{line_prev} = $self->{line};
1835     $self->{column_prev} = $self->{column};
1836     $self->{column}++;
1837 wakaba 1.179 $self->{nc}
1838 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1839     } else {
1840 wakaba 1.179 $self->{set_nc}->($self);
1841 wakaba 1.175 }
1842 wakaba 1.1
1843     redo A;
1844 wakaba 1.179 } elsif ($self->{nc} == -1) {
1845 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1846 wakaba 1.1 $before_leave->();
1847 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1848 wakaba 1.77
1849 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1850     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1851 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1852 wakaba 1.179 if ($self->{ct}->{attributes}) {
1853 wakaba 1.77
1854 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1855 wakaba 1.77 } else {
1856 wakaba 1.78 ## NOTE: This state should never be reached.
1857 wakaba 1.77
1858 wakaba 1.1 }
1859     } else {
1860 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1861 wakaba 1.1 }
1862 wakaba 1.59 $self->{state} = DATA_STATE;
1863 wakaba 1.1 # reconsume
1864    
1865 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1866 wakaba 1.1
1867     redo A;
1868     } else {
1869 wakaba 1.179 if ($self->{nc} == 0x0022 or # "
1870     $self->{nc} == 0x0027) { # '
1871 wakaba 1.77
1872 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
1873 wakaba 1.77 } else {
1874    
1875 wakaba 1.72 }
1876 wakaba 1.179 $self->{ca}->{name} .= chr ($self->{nc});
1877 wakaba 1.1 ## Stay in the state
1878    
1879 wakaba 1.175 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 wakaba 1.179 $self->{nc}
1884 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1885     } else {
1886 wakaba 1.179 $self->{set_nc}->($self);
1887 wakaba 1.175 }
1888 wakaba 1.1
1889     redo A;
1890     }
1891 wakaba 1.59 } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1892 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
1893     $self->{nc} == 0x000A or # LF
1894     $self->{nc} == 0x000B or # VT
1895     $self->{nc} == 0x000C or # FF
1896     $self->{nc} == 0x0020) { # SP
1897 wakaba 1.77
1898 wakaba 1.1 ## Stay in the state
1899    
1900 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1901     $self->{line_prev} = $self->{line};
1902     $self->{column_prev} = $self->{column};
1903     $self->{column}++;
1904 wakaba 1.179 $self->{nc}
1905 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1906     } else {
1907 wakaba 1.179 $self->{set_nc}->($self);
1908 wakaba 1.175 }
1909 wakaba 1.1
1910     redo A;
1911 wakaba 1.179 } elsif ($self->{nc} == 0x003D) { # =
1912 wakaba 1.77
1913 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1914 wakaba 1.1
1915 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1916     $self->{line_prev} = $self->{line};
1917     $self->{column_prev} = $self->{column};
1918     $self->{column}++;
1919 wakaba 1.179 $self->{nc}
1920 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1921     } else {
1922 wakaba 1.179 $self->{set_nc}->($self);
1923 wakaba 1.175 }
1924 wakaba 1.1
1925     redo A;
1926 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1927     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1928 wakaba 1.77
1929 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1930     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1931 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1932 wakaba 1.179 if ($self->{ct}->{attributes}) {
1933 wakaba 1.77
1934 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1935 wakaba 1.77 } else {
1936 wakaba 1.78 ## NOTE: This state should never be reached.
1937 wakaba 1.77
1938 wakaba 1.1 }
1939     } else {
1940 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1941 wakaba 1.1 }
1942 wakaba 1.59 $self->{state} = DATA_STATE;
1943 wakaba 1.1
1944 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1945     $self->{line_prev} = $self->{line};
1946     $self->{column_prev} = $self->{column};
1947     $self->{column}++;
1948 wakaba 1.179 $self->{nc}
1949 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1950     } else {
1951 wakaba 1.179 $self->{set_nc}->($self);
1952 wakaba 1.175 }
1953 wakaba 1.1
1954    
1955 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1956 wakaba 1.1
1957     redo A;
1958 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1959     $self->{nc} <= 0x005A) { # A..Z
1960 wakaba 1.77
1961 wakaba 1.179 $self->{ca}
1962     = {name => chr ($self->{nc} + 0x0020),
1963 wakaba 1.117 value => '',
1964     line => $self->{line}, column => $self->{column}};
1965 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
1966 wakaba 1.1
1967 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1968     $self->{line_prev} = $self->{line};
1969     $self->{column_prev} = $self->{column};
1970     $self->{column}++;
1971 wakaba 1.179 $self->{nc}
1972 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1973     } else {
1974 wakaba 1.179 $self->{set_nc}->($self);
1975 wakaba 1.175 }
1976 wakaba 1.1
1977     redo A;
1978 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1979 wakaba 1.1
1980 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1981    
1982 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1983     $self->{line_prev} = $self->{line};
1984     $self->{column_prev} = $self->{column};
1985     $self->{column}++;
1986 wakaba 1.179 $self->{nc}
1987 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1988     } else {
1989 wakaba 1.179 $self->{set_nc}->($self);
1990 wakaba 1.175 }
1991 wakaba 1.1
1992     redo A;
1993 wakaba 1.179 } elsif ($self->{nc} == -1) {
1994 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1995 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1996 wakaba 1.77
1997 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1998     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1999 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2000 wakaba 1.179 if ($self->{ct}->{attributes}) {
2001 wakaba 1.77
2002 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2003 wakaba 1.77 } else {
2004 wakaba 1.78 ## NOTE: This state should never be reached.
2005 wakaba 1.77
2006 wakaba 1.1 }
2007     } else {
2008 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2009 wakaba 1.1 }
2010 wakaba 1.59 $self->{state} = DATA_STATE;
2011 wakaba 1.1 # reconsume
2012    
2013 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2014 wakaba 1.1
2015     redo A;
2016     } else {
2017 wakaba 1.179 if ($self->{nc} == 0x0022 or # "
2018     $self->{nc} == 0x0027) { # '
2019 wakaba 1.153
2020     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
2021     } else {
2022    
2023     }
2024 wakaba 1.179 $self->{ca}
2025     = {name => chr ($self->{nc}),
2026 wakaba 1.117 value => '',
2027     line => $self->{line}, column => $self->{column}};
2028 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
2029 wakaba 1.1
2030 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2031     $self->{line_prev} = $self->{line};
2032     $self->{column_prev} = $self->{column};
2033     $self->{column}++;
2034 wakaba 1.179 $self->{nc}
2035 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2036     } else {
2037 wakaba 1.179 $self->{set_nc}->($self);
2038 wakaba 1.175 }
2039 wakaba 1.1
2040     redo A;
2041     }
2042 wakaba 1.59 } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
2043 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
2044     $self->{nc} == 0x000A or # LF
2045     $self->{nc} == 0x000B or # VT
2046     $self->{nc} == 0x000C or # FF
2047     $self->{nc} == 0x0020) { # SP
2048 wakaba 1.77
2049 wakaba 1.1 ## Stay in the state
2050    
2051 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2052     $self->{line_prev} = $self->{line};
2053     $self->{column_prev} = $self->{column};
2054     $self->{column}++;
2055 wakaba 1.179 $self->{nc}
2056 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2057     } else {
2058 wakaba 1.179 $self->{set_nc}->($self);
2059 wakaba 1.175 }
2060 wakaba 1.1
2061     redo A;
2062 wakaba 1.179 } elsif ($self->{nc} == 0x0022) { # "
2063 wakaba 1.77
2064 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
2065 wakaba 1.1
2066 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2067     $self->{line_prev} = $self->{line};
2068     $self->{column_prev} = $self->{column};
2069     $self->{column}++;
2070 wakaba 1.179 $self->{nc}
2071 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2072     } else {
2073 wakaba 1.179 $self->{set_nc}->($self);
2074 wakaba 1.175 }
2075 wakaba 1.1
2076     redo A;
2077 wakaba 1.179 } elsif ($self->{nc} == 0x0026) { # &
2078 wakaba 1.77
2079 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
2080 wakaba 1.1 ## reconsume
2081     redo A;
2082 wakaba 1.179 } elsif ($self->{nc} == 0x0027) { # '
2083 wakaba 1.77
2084 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
2085 wakaba 1.1
2086 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2087     $self->{line_prev} = $self->{line};
2088     $self->{column_prev} = $self->{column};
2089     $self->{column}++;
2090 wakaba 1.179 $self->{nc}
2091 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2092     } else {
2093 wakaba 1.179 $self->{set_nc}->($self);
2094 wakaba 1.175 }
2095 wakaba 1.1
2096     redo A;
2097 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2098 wakaba 1.153 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty unquoted attribute value');
2099 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2100 wakaba 1.77
2101 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2102     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2103 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2104 wakaba 1.179 if ($self->{ct}->{attributes}) {
2105 wakaba 1.77
2106 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2107 wakaba 1.77 } else {
2108 wakaba 1.78 ## NOTE: This state should never be reached.
2109 wakaba 1.77
2110 wakaba 1.1 }
2111     } else {
2112 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2113 wakaba 1.1 }
2114 wakaba 1.59 $self->{state} = DATA_STATE;
2115 wakaba 1.1
2116 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2117     $self->{line_prev} = $self->{line};
2118     $self->{column_prev} = $self->{column};
2119     $self->{column}++;
2120 wakaba 1.179 $self->{nc}
2121 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2122     } else {
2123 wakaba 1.179 $self->{set_nc}->($self);
2124 wakaba 1.175 }
2125 wakaba 1.1
2126    
2127 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2128 wakaba 1.1
2129     redo A;
2130 wakaba 1.179 } elsif ($self->{nc} == -1) {
2131 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2132 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2133 wakaba 1.77
2134 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2135     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2136 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2137 wakaba 1.179 if ($self->{ct}->{attributes}) {
2138 wakaba 1.77
2139 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2140 wakaba 1.77 } else {
2141 wakaba 1.78 ## NOTE: This state should never be reached.
2142 wakaba 1.77
2143 wakaba 1.1 }
2144     } else {
2145 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2146 wakaba 1.1 }
2147 wakaba 1.59 $self->{state} = DATA_STATE;
2148 wakaba 1.1 ## reconsume
2149    
2150 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2151 wakaba 1.1
2152     redo A;
2153     } else {
2154 wakaba 1.179 if ($self->{nc} == 0x003D) { # =
2155 wakaba 1.77
2156 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute value');
2157 wakaba 1.77 } else {
2158    
2159 wakaba 1.72 }
2160 wakaba 1.179 $self->{ca}->{value} .= chr ($self->{nc});
2161 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
2162 wakaba 1.1
2163 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2164     $self->{line_prev} = $self->{line};
2165     $self->{column_prev} = $self->{column};
2166     $self->{column}++;
2167 wakaba 1.179 $self->{nc}
2168 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2169     } else {
2170 wakaba 1.179 $self->{set_nc}->($self);
2171 wakaba 1.175 }
2172 wakaba 1.1
2173     redo A;
2174     }
2175 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
2176 wakaba 1.179 if ($self->{nc} == 0x0022) { # "
2177 wakaba 1.77
2178 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
2179 wakaba 1.1
2180 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2181     $self->{line_prev} = $self->{line};
2182     $self->{column_prev} = $self->{column};
2183     $self->{column}++;
2184 wakaba 1.179 $self->{nc}
2185 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2186     } else {
2187 wakaba 1.179 $self->{set_nc}->($self);
2188 wakaba 1.175 }
2189 wakaba 1.1
2190     redo A;
2191 wakaba 1.179 } elsif ($self->{nc} == 0x0026) { # &
2192 wakaba 1.77
2193 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2194     ## "entity in attribute value state". In this implementation, the
2195     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2196     ## implementation of the "consume a character reference" algorithm.
2197 wakaba 1.165 $self->{prev_state} = $self->{state};
2198 wakaba 1.179 $self->{entity_add} = 0x0022; # "
2199 wakaba 1.163 $self->{state} = ENTITY_STATE;
2200 wakaba 1.1
2201 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2202     $self->{line_prev} = $self->{line};
2203     $self->{column_prev} = $self->{column};
2204     $self->{column}++;
2205 wakaba 1.179 $self->{nc}
2206 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2207     } else {
2208 wakaba 1.179 $self->{set_nc}->($self);
2209 wakaba 1.175 }
2210 wakaba 1.1
2211     redo A;
2212 wakaba 1.179 } elsif ($self->{nc} == -1) {
2213 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed attribute value');
2214 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2215 wakaba 1.77
2216 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2217     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2218 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2219 wakaba 1.179 if ($self->{ct}->{attributes}) {
2220 wakaba 1.77
2221 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2222 wakaba 1.77 } else {
2223 wakaba 1.78 ## NOTE: This state should never be reached.
2224 wakaba 1.77
2225 wakaba 1.1 }
2226     } else {
2227 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2228 wakaba 1.1 }
2229 wakaba 1.59 $self->{state} = DATA_STATE;
2230 wakaba 1.1 ## reconsume
2231    
2232 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2233 wakaba 1.1
2234     redo A;
2235     } else {
2236 wakaba 1.77
2237 wakaba 1.179 $self->{ca}->{value} .= chr ($self->{nc});
2238     $self->{read_until}->($self->{ca}->{value},
2239 wakaba 1.169 q["&],
2240 wakaba 1.179 length $self->{ca}->{value});
2241 wakaba 1.169
2242 wakaba 1.1 ## Stay in the state
2243    
2244 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2245     $self->{line_prev} = $self->{line};
2246     $self->{column_prev} = $self->{column};
2247     $self->{column}++;
2248 wakaba 1.179 $self->{nc}
2249 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2250     } else {
2251 wakaba 1.179 $self->{set_nc}->($self);
2252 wakaba 1.175 }
2253 wakaba 1.1
2254     redo A;
2255     }
2256 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
2257 wakaba 1.179 if ($self->{nc} == 0x0027) { # '
2258 wakaba 1.77
2259 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
2260 wakaba 1.1
2261 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2262     $self->{line_prev} = $self->{line};
2263     $self->{column_prev} = $self->{column};
2264     $self->{column}++;
2265 wakaba 1.179 $self->{nc}
2266 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2267     } else {
2268 wakaba 1.179 $self->{set_nc}->($self);
2269 wakaba 1.175 }
2270 wakaba 1.1
2271     redo A;
2272 wakaba 1.179 } elsif ($self->{nc} == 0x0026) { # &
2273 wakaba 1.77
2274 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2275     ## "entity in attribute value state". In this implementation, the
2276     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2277     ## implementation of the "consume a character reference" algorithm.
2278 wakaba 1.179 $self->{entity_add} = 0x0027; # '
2279 wakaba 1.165 $self->{prev_state} = $self->{state};
2280 wakaba 1.163 $self->{state} = ENTITY_STATE;
2281 wakaba 1.1
2282 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2283     $self->{line_prev} = $self->{line};
2284     $self->{column_prev} = $self->{column};
2285     $self->{column}++;
2286 wakaba 1.179 $self->{nc}
2287 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2288     } else {
2289 wakaba 1.179 $self->{set_nc}->($self);
2290 wakaba 1.175 }
2291 wakaba 1.1
2292     redo A;
2293 wakaba 1.179 } elsif ($self->{nc} == -1) {
2294 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed attribute value');
2295 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2296 wakaba 1.77
2297 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2298     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2299 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2300 wakaba 1.179 if ($self->{ct}->{attributes}) {
2301 wakaba 1.77
2302 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2303 wakaba 1.77 } else {
2304 wakaba 1.78 ## NOTE: This state should never be reached.
2305 wakaba 1.77
2306 wakaba 1.1 }
2307     } else {
2308 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2309 wakaba 1.1 }
2310 wakaba 1.59 $self->{state} = DATA_STATE;
2311 wakaba 1.1 ## reconsume
2312    
2313 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2314 wakaba 1.1
2315     redo A;
2316     } else {
2317 wakaba 1.77
2318 wakaba 1.179 $self->{ca}->{value} .= chr ($self->{nc});
2319     $self->{read_until}->($self->{ca}->{value},
2320 wakaba 1.169 q['&],
2321 wakaba 1.179 length $self->{ca}->{value});
2322 wakaba 1.169
2323 wakaba 1.1 ## Stay in the state
2324    
2325 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2326     $self->{line_prev} = $self->{line};
2327     $self->{column_prev} = $self->{column};
2328     $self->{column}++;
2329 wakaba 1.179 $self->{nc}
2330 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2331     } else {
2332 wakaba 1.179 $self->{set_nc}->($self);
2333 wakaba 1.175 }
2334 wakaba 1.1
2335     redo A;
2336     }
2337 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
2338 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
2339     $self->{nc} == 0x000A or # LF
2340     $self->{nc} == 0x000B or # HT
2341     $self->{nc} == 0x000C or # FF
2342     $self->{nc} == 0x0020) { # SP
2343 wakaba 1.77
2344 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2345 wakaba 1.1
2346 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2347     $self->{line_prev} = $self->{line};
2348     $self->{column_prev} = $self->{column};
2349     $self->{column}++;
2350 wakaba 1.179 $self->{nc}
2351 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2352     } else {
2353 wakaba 1.179 $self->{set_nc}->($self);
2354 wakaba 1.175 }
2355 wakaba 1.1
2356     redo A;
2357 wakaba 1.179 } elsif ($self->{nc} == 0x0026) { # &
2358 wakaba 1.77
2359 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2360     ## "entity in attribute value state". In this implementation, the
2361     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2362     ## implementation of the "consume a character reference" algorithm.
2363 wakaba 1.179 $self->{entity_add} = -1;
2364 wakaba 1.165 $self->{prev_state} = $self->{state};
2365 wakaba 1.163 $self->{state} = ENTITY_STATE;
2366 wakaba 1.1
2367 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2368     $self->{line_prev} = $self->{line};
2369     $self->{column_prev} = $self->{column};
2370     $self->{column}++;
2371 wakaba 1.179 $self->{nc}
2372 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2373     } else {
2374 wakaba 1.179 $self->{set_nc}->($self);
2375 wakaba 1.175 }
2376 wakaba 1.1
2377     redo A;
2378 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2379     if ($self->{ct}->{type} == START_TAG_TOKEN) {
2380 wakaba 1.77
2381 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2382     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2383 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2384 wakaba 1.179 if ($self->{ct}->{attributes}) {
2385 wakaba 1.77
2386 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2387 wakaba 1.77 } else {
2388 wakaba 1.78 ## NOTE: This state should never be reached.
2389 wakaba 1.77
2390 wakaba 1.1 }
2391     } else {
2392 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2393 wakaba 1.1 }
2394 wakaba 1.59 $self->{state} = DATA_STATE;
2395 wakaba 1.1
2396 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2397     $self->{line_prev} = $self->{line};
2398     $self->{column_prev} = $self->{column};
2399     $self->{column}++;
2400 wakaba 1.179 $self->{nc}
2401 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2402     } else {
2403 wakaba 1.179 $self->{set_nc}->($self);
2404 wakaba 1.175 }
2405 wakaba 1.1
2406    
2407 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2408 wakaba 1.1
2409     redo A;
2410 wakaba 1.179 } elsif ($self->{nc} == -1) {
2411 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2412 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2413 wakaba 1.77
2414 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2415     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2416 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2417 wakaba 1.179 if ($self->{ct}->{attributes}) {
2418 wakaba 1.77
2419 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2420 wakaba 1.77 } else {
2421 wakaba 1.78 ## NOTE: This state should never be reached.
2422 wakaba 1.77
2423 wakaba 1.1 }
2424     } else {
2425 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2426 wakaba 1.1 }
2427 wakaba 1.59 $self->{state} = DATA_STATE;
2428 wakaba 1.1 ## reconsume
2429    
2430 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2431 wakaba 1.1
2432     redo A;
2433     } else {
2434 wakaba 1.72 if ({
2435     0x0022 => 1, # "
2436     0x0027 => 1, # '
2437     0x003D => 1, # =
2438 wakaba 1.179 }->{$self->{nc}}) {
2439 wakaba 1.77
2440 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute value');
2441 wakaba 1.77 } else {
2442    
2443 wakaba 1.72 }
2444 wakaba 1.179 $self->{ca}->{value} .= chr ($self->{nc});
2445     $self->{read_until}->($self->{ca}->{value},
2446 wakaba 1.169 q["'=& >],
2447 wakaba 1.179 length $self->{ca}->{value});
2448 wakaba 1.169
2449 wakaba 1.1 ## Stay in the state
2450    
2451 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2452     $self->{line_prev} = $self->{line};
2453     $self->{column_prev} = $self->{column};
2454     $self->{column}++;
2455 wakaba 1.179 $self->{nc}
2456 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2457     } else {
2458 wakaba 1.179 $self->{set_nc}->($self);
2459 wakaba 1.175 }
2460 wakaba 1.1
2461     redo A;
2462     }
2463 wakaba 1.72 } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2464 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
2465     $self->{nc} == 0x000A or # LF
2466     $self->{nc} == 0x000B or # VT
2467     $self->{nc} == 0x000C or # FF
2468     $self->{nc} == 0x0020) { # SP
2469 wakaba 1.77
2470 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2471    
2472 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2473     $self->{line_prev} = $self->{line};
2474     $self->{column_prev} = $self->{column};
2475     $self->{column}++;
2476 wakaba 1.179 $self->{nc}
2477 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2478     } else {
2479 wakaba 1.179 $self->{set_nc}->($self);
2480 wakaba 1.175 }
2481 wakaba 1.72
2482     redo A;
2483 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2484     if ($self->{ct}->{type} == START_TAG_TOKEN) {
2485 wakaba 1.77
2486 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2487     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2488 wakaba 1.72 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2489 wakaba 1.179 if ($self->{ct}->{attributes}) {
2490 wakaba 1.77
2491 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2492 wakaba 1.77 } else {
2493 wakaba 1.78 ## NOTE: This state should never be reached.
2494 wakaba 1.77
2495 wakaba 1.72 }
2496     } else {
2497 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2498 wakaba 1.72 }
2499     $self->{state} = DATA_STATE;
2500    
2501 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2502     $self->{line_prev} = $self->{line};
2503     $self->{column_prev} = $self->{column};
2504     $self->{column}++;
2505 wakaba 1.179 $self->{nc}
2506 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2507     } else {
2508 wakaba 1.179 $self->{set_nc}->($self);
2509 wakaba 1.175 }
2510 wakaba 1.72
2511    
2512 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2513 wakaba 1.72
2514     redo A;
2515 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
2516 wakaba 1.72
2517 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
2518    
2519 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2520     $self->{line_prev} = $self->{line};
2521     $self->{column_prev} = $self->{column};
2522     $self->{column}++;
2523 wakaba 1.179 $self->{nc}
2524 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2525     } else {
2526 wakaba 1.179 $self->{set_nc}->($self);
2527 wakaba 1.175 }
2528 wakaba 1.72
2529 wakaba 1.122 redo A;
2530 wakaba 1.179 } elsif ($self->{nc} == -1) {
2531 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2532 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2533 wakaba 1.138
2534 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2535     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2536     if ($self->{ct}->{attributes}) {
2537 wakaba 1.138
2538 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2539 wakaba 1.138 } else {
2540     ## NOTE: This state should never be reached.
2541    
2542     }
2543     } else {
2544 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2545 wakaba 1.138 }
2546     $self->{state} = DATA_STATE;
2547     ## Reconsume.
2548 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2549 wakaba 1.138 redo A;
2550 wakaba 1.122 } else {
2551    
2552 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no space between attributes');
2553 wakaba 1.122 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2554     ## reconsume
2555     redo A;
2556     }
2557     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2558 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
2559     if ($self->{ct}->{type} == END_TAG_TOKEN) {
2560 wakaba 1.77
2561 wakaba 1.179 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc', token => $self->{ct});
2562 wakaba 1.122 ## TODO: Different type than slash in start tag
2563     $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2564 wakaba 1.179 if ($self->{ct}->{attributes}) {
2565 wakaba 1.122
2566 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2567 wakaba 1.122 } else {
2568    
2569     }
2570     ## TODO: Test |<title></title/>|
2571 wakaba 1.72 } else {
2572 wakaba 1.77
2573 wakaba 1.122 $self->{self_closing} = 1;
2574 wakaba 1.72 }
2575 wakaba 1.122
2576     $self->{state} = DATA_STATE;
2577    
2578 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2579     $self->{line_prev} = $self->{line};
2580     $self->{column_prev} = $self->{column};
2581     $self->{column}++;
2582 wakaba 1.179 $self->{nc}
2583 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2584     } else {
2585 wakaba 1.179 $self->{set_nc}->($self);
2586 wakaba 1.175 }
2587 wakaba 1.122
2588    
2589 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2590 wakaba 1.122
2591 wakaba 1.72 redo A;
2592 wakaba 1.179 } elsif ($self->{nc} == -1) {
2593 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2594 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2595 wakaba 1.138
2596 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2597     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2598     if ($self->{ct}->{attributes}) {
2599 wakaba 1.138
2600 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2601 wakaba 1.138 } else {
2602     ## NOTE: This state should never be reached.
2603    
2604     }
2605     } else {
2606 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2607 wakaba 1.138 }
2608     $self->{state} = DATA_STATE;
2609     ## Reconsume.
2610 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2611 wakaba 1.138 redo A;
2612 wakaba 1.72 } else {
2613 wakaba 1.77
2614 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc');
2615 wakaba 1.122 ## TODO: This error type is wrong.
2616 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2617 wakaba 1.122 ## Reconsume.
2618 wakaba 1.72 redo A;
2619     }
2620 wakaba 1.59 } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2621 wakaba 1.1 ## (only happen if PCDATA state)
2622 wakaba 1.163
2623     ## NOTE: Unlike spec's "bogus comment state", this implementation
2624     ## consumes characters one-by-one basis.
2625 wakaba 1.1
2626 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
2627 wakaba 1.163
2628     $self->{state} = DATA_STATE;
2629    
2630 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2631     $self->{line_prev} = $self->{line};
2632     $self->{column_prev} = $self->{column};
2633     $self->{column}++;
2634 wakaba 1.179 $self->{nc}
2635 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2636     } else {
2637 wakaba 1.179 $self->{set_nc}->($self);
2638 wakaba 1.175 }
2639 wakaba 1.1
2640    
2641 wakaba 1.179 return ($self->{ct}); # comment
2642 wakaba 1.163 redo A;
2643 wakaba 1.179 } elsif ($self->{nc} == -1) {
2644 wakaba 1.163
2645     $self->{state} = DATA_STATE;
2646     ## reconsume
2647 wakaba 1.1
2648 wakaba 1.179 return ($self->{ct}); # comment
2649 wakaba 1.163 redo A;
2650     } else {
2651    
2652 wakaba 1.179 $self->{ct}->{data} .= chr ($self->{nc}); # comment
2653     $self->{read_until}->($self->{ct}->{data},
2654 wakaba 1.169 q[>],
2655 wakaba 1.179 length $self->{ct}->{data});
2656 wakaba 1.169
2657 wakaba 1.163 ## Stay in the state.
2658    
2659 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2660     $self->{line_prev} = $self->{line};
2661     $self->{column_prev} = $self->{column};
2662     $self->{column}++;
2663 wakaba 1.179 $self->{nc}
2664 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2665     } else {
2666 wakaba 1.179 $self->{set_nc}->($self);
2667 wakaba 1.175 }
2668 wakaba 1.1
2669 wakaba 1.163 redo A;
2670     }
2671 wakaba 1.59 } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2672 wakaba 1.1 ## (only happen if PCDATA state)
2673    
2674 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
2675 wakaba 1.1
2676 wakaba 1.159 $self->{state} = MD_HYPHEN_STATE;
2677    
2678 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2679     $self->{line_prev} = $self->{line};
2680     $self->{column_prev} = $self->{column};
2681     $self->{column}++;
2682 wakaba 1.179 $self->{nc}
2683 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2684     } else {
2685 wakaba 1.179 $self->{set_nc}->($self);
2686 wakaba 1.175 }
2687 wakaba 1.1
2688 wakaba 1.159 redo A;
2689 wakaba 1.179 } elsif ($self->{nc} == 0x0044 or # D
2690     $self->{nc} == 0x0064) { # d
2691 wakaba 1.159 ## ASCII case-insensitive.
2692    
2693     $self->{state} = MD_DOCTYPE_STATE;
2694 wakaba 1.179 $self->{s_kwd} = chr $self->{nc};
2695 wakaba 1.1
2696 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2697     $self->{line_prev} = $self->{line};
2698     $self->{column_prev} = $self->{column};
2699     $self->{column}++;
2700 wakaba 1.179 $self->{nc}
2701 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2702     } else {
2703 wakaba 1.179 $self->{set_nc}->($self);
2704 wakaba 1.175 }
2705 wakaba 1.1
2706 wakaba 1.159 redo A;
2707     } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2708     $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2709 wakaba 1.179 $self->{nc} == 0x005B) { # [
2710 wakaba 1.159
2711     $self->{state} = MD_CDATA_STATE;
2712 wakaba 1.179 $self->{s_kwd} = '[';
2713 wakaba 1.159
2714 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2715     $self->{line_prev} = $self->{line};
2716     $self->{column_prev} = $self->{column};
2717     $self->{column}++;
2718 wakaba 1.179 $self->{nc}
2719 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2720     } else {
2721 wakaba 1.179 $self->{set_nc}->($self);
2722 wakaba 1.175 }
2723 wakaba 1.1
2724 wakaba 1.159 redo A;
2725 wakaba 1.1 } else {
2726 wakaba 1.159
2727 wakaba 1.1 }
2728 wakaba 1.159
2729     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2730     line => $self->{line_prev},
2731 wakaba 1.160 column => $self->{column_prev} - 1);
2732 wakaba 1.159 ## Reconsume.
2733     $self->{state} = BOGUS_COMMENT_STATE;
2734 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN, data => '',
2735 wakaba 1.159 line => $self->{line_prev},
2736     column => $self->{column_prev} - 1,
2737     };
2738     redo A;
2739     } elsif ($self->{state} == MD_HYPHEN_STATE) {
2740 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
2741 wakaba 1.159
2742 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN, data => '',
2743 wakaba 1.159 line => $self->{line_prev},
2744     column => $self->{column_prev} - 2,
2745     };
2746     $self->{state} = COMMENT_START_STATE;
2747    
2748 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2749     $self->{line_prev} = $self->{line};
2750     $self->{column_prev} = $self->{column};
2751     $self->{column}++;
2752 wakaba 1.179 $self->{nc}
2753 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2754     } else {
2755 wakaba 1.179 $self->{set_nc}->($self);
2756 wakaba 1.175 }
2757 wakaba 1.1
2758 wakaba 1.159 redo A;
2759 wakaba 1.1 } else {
2760 wakaba 1.159
2761     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2762     line => $self->{line_prev},
2763     column => $self->{column_prev} - 2);
2764     $self->{state} = BOGUS_COMMENT_STATE;
2765     ## Reconsume.
2766 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN,
2767 wakaba 1.159 data => '-',
2768     line => $self->{line_prev},
2769     column => $self->{column_prev} - 2,
2770     };
2771     redo A;
2772 wakaba 1.1 }
2773 wakaba 1.159 } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2774     ## ASCII case-insensitive.
2775 wakaba 1.179 if ($self->{nc} == [
2776 wakaba 1.159 undef,
2777     0x004F, # O
2778     0x0043, # C
2779     0x0054, # T
2780     0x0059, # Y
2781     0x0050, # P
2782 wakaba 1.179 ]->[length $self->{s_kwd}] or
2783     $self->{nc} == [
2784 wakaba 1.159 undef,
2785     0x006F, # o
2786     0x0063, # c
2787     0x0074, # t
2788     0x0079, # y
2789     0x0070, # p
2790 wakaba 1.179 ]->[length $self->{s_kwd}]) {
2791 wakaba 1.159
2792     ## Stay in the state.
2793 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
2794 wakaba 1.159
2795 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2796     $self->{line_prev} = $self->{line};
2797     $self->{column_prev} = $self->{column};
2798     $self->{column}++;
2799 wakaba 1.179 $self->{nc}
2800 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2801     } else {
2802 wakaba 1.179 $self->{set_nc}->($self);
2803 wakaba 1.175 }
2804 wakaba 1.1
2805 wakaba 1.159 redo A;
2806 wakaba 1.179 } elsif ((length $self->{s_kwd}) == 6 and
2807     ($self->{nc} == 0x0045 or # E
2808     $self->{nc} == 0x0065)) { # e
2809 wakaba 1.159
2810     $self->{state} = DOCTYPE_STATE;
2811 wakaba 1.179 $self->{ct} = {type => DOCTYPE_TOKEN,
2812 wakaba 1.159 quirks => 1,
2813     line => $self->{line_prev},
2814     column => $self->{column_prev} - 7,
2815     };
2816 wakaba 1.124
2817 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2818     $self->{line_prev} = $self->{line};
2819     $self->{column_prev} = $self->{column};
2820     $self->{column}++;
2821 wakaba 1.179 $self->{nc}
2822 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2823     } else {
2824 wakaba 1.179 $self->{set_nc}->($self);
2825 wakaba 1.175 }
2826 wakaba 1.124
2827 wakaba 1.159 redo A;
2828 wakaba 1.124 } else {
2829    
2830 wakaba 1.159 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2831     line => $self->{line_prev},
2832 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd});
2833 wakaba 1.159 $self->{state} = BOGUS_COMMENT_STATE;
2834     ## Reconsume.
2835 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN,
2836     data => $self->{s_kwd},
2837 wakaba 1.159 line => $self->{line_prev},
2838 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd},
2839 wakaba 1.159 };
2840     redo A;
2841 wakaba 1.124 }
2842 wakaba 1.159 } elsif ($self->{state} == MD_CDATA_STATE) {
2843 wakaba 1.179 if ($self->{nc} == {
2844 wakaba 1.159 '[' => 0x0043, # C
2845     '[C' => 0x0044, # D
2846     '[CD' => 0x0041, # A
2847     '[CDA' => 0x0054, # T
2848     '[CDAT' => 0x0041, # A
2849 wakaba 1.179 }->{$self->{s_kwd}}) {
2850 wakaba 1.159
2851     ## Stay in the state.
2852 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
2853 wakaba 1.159
2854 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2855     $self->{line_prev} = $self->{line};
2856     $self->{column_prev} = $self->{column};
2857     $self->{column}++;
2858 wakaba 1.179 $self->{nc}
2859 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2860     } else {
2861 wakaba 1.179 $self->{set_nc}->($self);
2862 wakaba 1.175 }
2863 wakaba 1.124
2864 wakaba 1.159 redo A;
2865 wakaba 1.179 } elsif ($self->{s_kwd} eq '[CDATA' and
2866     $self->{nc} == 0x005B) { # [
2867 wakaba 1.159
2868 wakaba 1.179 $self->{ct} = {type => CHARACTER_TOKEN,
2869 wakaba 1.161 data => '',
2870     line => $self->{line_prev},
2871     column => $self->{column_prev} - 7};
2872     $self->{state} = CDATA_SECTION_STATE;
2873 wakaba 1.159
2874 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2875     $self->{line_prev} = $self->{line};
2876     $self->{column_prev} = $self->{column};
2877     $self->{column}++;
2878 wakaba 1.179 $self->{nc}
2879 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2880     } else {
2881 wakaba 1.179 $self->{set_nc}->($self);
2882 wakaba 1.175 }
2883 wakaba 1.124
2884 wakaba 1.159 redo A;
2885 wakaba 1.77 } else {
2886    
2887 wakaba 1.159 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2888     line => $self->{line_prev},
2889 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd});
2890 wakaba 1.159 $self->{state} = BOGUS_COMMENT_STATE;
2891     ## Reconsume.
2892 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN,
2893     data => $self->{s_kwd},
2894 wakaba 1.159 line => $self->{line_prev},
2895 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd},
2896 wakaba 1.159 };
2897     redo A;
2898 wakaba 1.1 }
2899 wakaba 1.59 } elsif ($self->{state} == COMMENT_START_STATE) {
2900 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
2901 wakaba 1.77
2902 wakaba 1.59 $self->{state} = COMMENT_START_DASH_STATE;
2903 wakaba 1.23
2904 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2905     $self->{line_prev} = $self->{line};
2906     $self->{column_prev} = $self->{column};
2907     $self->{column}++;
2908 wakaba 1.179 $self->{nc}
2909 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2910     } else {
2911 wakaba 1.179 $self->{set_nc}->($self);
2912 wakaba 1.175 }
2913 wakaba 1.23
2914     redo A;
2915 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2916 wakaba 1.77
2917 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment');
2918 wakaba 1.59 $self->{state} = DATA_STATE;
2919 wakaba 1.23
2920 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2921     $self->{line_prev} = $self->{line};
2922     $self->{column_prev} = $self->{column};
2923     $self->{column}++;
2924 wakaba 1.179 $self->{nc}
2925 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2926     } else {
2927 wakaba 1.179 $self->{set_nc}->($self);
2928 wakaba 1.175 }
2929 wakaba 1.23
2930    
2931 wakaba 1.179 return ($self->{ct}); # comment
2932 wakaba 1.23
2933     redo A;
2934 wakaba 1.179 } elsif ($self->{nc} == -1) {
2935 wakaba 1.77
2936 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
2937 wakaba 1.59 $self->{state} = DATA_STATE;
2938 wakaba 1.23 ## reconsume
2939    
2940 wakaba 1.179 return ($self->{ct}); # comment
2941 wakaba 1.23
2942 wakaba 1.77 redo A;
2943     } else {
2944    
2945 wakaba 1.179 $self->{ct}->{data} # comment
2946     .= chr ($self->{nc});
2947 wakaba 1.59 $self->{state} = COMMENT_STATE;
2948 wakaba 1.23
2949 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2950     $self->{line_prev} = $self->{line};
2951     $self->{column_prev} = $self->{column};
2952     $self->{column}++;
2953 wakaba 1.179 $self->{nc}
2954 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2955     } else {
2956 wakaba 1.179 $self->{set_nc}->($self);
2957 wakaba 1.175 }
2958 wakaba 1.23
2959     redo A;
2960     }
2961 wakaba 1.59 } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2962 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
2963 wakaba 1.77
2964 wakaba 1.59 $self->{state} = COMMENT_END_STATE;
2965 wakaba 1.23
2966 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2967     $self->{line_prev} = $self->{line};
2968     $self->{column_prev} = $self->{column};
2969     $self->{column}++;
2970 wakaba 1.179 $self->{nc}
2971 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2972     } else {
2973 wakaba 1.179 $self->{set_nc}->($self);
2974 wakaba 1.175 }
2975 wakaba 1.23
2976     redo A;
2977 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2978 wakaba 1.77
2979 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment');
2980 wakaba 1.59 $self->{state} = DATA_STATE;
2981 wakaba 1.23
2982 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2983     $self->{line_prev} = $self->{line};
2984     $self->{column_prev} = $self->{column};
2985     $self->{column}++;
2986 wakaba 1.179 $self->{nc}
2987 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2988     } else {
2989 wakaba 1.179 $self->{set_nc}->($self);
2990 wakaba 1.175 }
2991 wakaba 1.23
2992    
2993 wakaba 1.179 return ($self->{ct}); # comment
2994 wakaba 1.23
2995     redo A;
2996 wakaba 1.179 } elsif ($self->{nc} == -1) {
2997 wakaba 1.77
2998 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
2999 wakaba 1.59 $self->{state} = DATA_STATE;
3000 wakaba 1.23 ## reconsume
3001    
3002 wakaba 1.179 return ($self->{ct}); # comment
3003 wakaba 1.23
3004     redo A;
3005     } else {
3006 wakaba 1.77
3007 wakaba 1.179 $self->{ct}->{data} # comment
3008     .= '-' . chr ($self->{nc});
3009 wakaba 1.59 $self->{state} = COMMENT_STATE;
3010 wakaba 1.23
3011 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3012     $self->{line_prev} = $self->{line};
3013     $self->{column_prev} = $self->{column};
3014     $self->{column}++;
3015 wakaba 1.179 $self->{nc}
3016 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3017     } else {
3018 wakaba 1.179 $self->{set_nc}->($self);
3019 wakaba 1.175 }
3020 wakaba 1.23
3021     redo A;
3022     }
3023 wakaba 1.59 } elsif ($self->{state} == COMMENT_STATE) {
3024 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
3025 wakaba 1.77
3026 wakaba 1.59 $self->{state} = COMMENT_END_DASH_STATE;
3027 wakaba 1.1
3028 wakaba 1.175 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 wakaba 1.179 $self->{nc}
3033 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3034     } else {
3035 wakaba 1.179 $self->{set_nc}->($self);
3036 wakaba 1.175 }
3037 wakaba 1.1
3038     redo A;
3039 wakaba 1.179 } elsif ($self->{nc} == -1) {
3040 wakaba 1.77
3041 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3042 wakaba 1.59 $self->{state} = DATA_STATE;
3043 wakaba 1.1 ## reconsume
3044    
3045 wakaba 1.179 return ($self->{ct}); # comment
3046 wakaba 1.1
3047     redo A;
3048     } else {
3049 wakaba 1.77
3050 wakaba 1.179 $self->{ct}->{data} .= chr ($self->{nc}); # comment
3051     $self->{read_until}->($self->{ct}->{data},
3052 wakaba 1.169 q[-],
3053 wakaba 1.179 length $self->{ct}->{data});
3054 wakaba 1.169
3055 wakaba 1.1 ## Stay in the state
3056    
3057 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3058     $self->{line_prev} = $self->{line};
3059     $self->{column_prev} = $self->{column};
3060     $self->{column}++;
3061 wakaba 1.179 $self->{nc}
3062 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3063     } else {
3064 wakaba 1.179 $self->{set_nc}->($self);
3065 wakaba 1.175 }
3066 wakaba 1.1
3067     redo A;
3068     }
3069 wakaba 1.59 } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
3070 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
3071 wakaba 1.77
3072 wakaba 1.59 $self->{state} = COMMENT_END_STATE;
3073 wakaba 1.1
3074 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3075     $self->{line_prev} = $self->{line};
3076     $self->{column_prev} = $self->{column};
3077     $self->{column}++;
3078 wakaba 1.179 $self->{nc}
3079 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3080     } else {
3081 wakaba 1.179 $self->{set_nc}->($self);
3082 wakaba 1.175 }
3083 wakaba 1.1
3084     redo A;
3085 wakaba 1.179 } elsif ($self->{nc} == -1) {
3086 wakaba 1.77
3087 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3088 wakaba 1.59 $self->{state} = DATA_STATE;
3089 wakaba 1.1 ## reconsume
3090    
3091 wakaba 1.179 return ($self->{ct}); # comment
3092 wakaba 1.1
3093     redo A;
3094     } else {
3095 wakaba 1.77
3096 wakaba 1.179 $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
3097 wakaba 1.59 $self->{state} = COMMENT_STATE;
3098 wakaba 1.1
3099 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3100     $self->{line_prev} = $self->{line};
3101     $self->{column_prev} = $self->{column};
3102     $self->{column}++;
3103 wakaba 1.179 $self->{nc}
3104 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3105     } else {
3106 wakaba 1.179 $self->{set_nc}->($self);
3107 wakaba 1.175 }
3108 wakaba 1.1
3109     redo A;
3110     }
3111 wakaba 1.59 } elsif ($self->{state} == COMMENT_END_STATE) {
3112 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
3113 wakaba 1.77
3114 wakaba 1.59 $self->{state} = DATA_STATE;
3115 wakaba 1.1
3116 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3117     $self->{line_prev} = $self->{line};
3118     $self->{column_prev} = $self->{column};
3119     $self->{column}++;
3120 wakaba 1.179 $self->{nc}
3121 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3122     } else {
3123 wakaba 1.179 $self->{set_nc}->($self);
3124 wakaba 1.175 }
3125 wakaba 1.1
3126    
3127 wakaba 1.179 return ($self->{ct}); # comment
3128 wakaba 1.1
3129     redo A;
3130 wakaba 1.179 } elsif ($self->{nc} == 0x002D) { # -
3131 wakaba 1.77
3132 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'dash in comment',
3133 wakaba 1.112 line => $self->{line_prev},
3134     column => $self->{column_prev});
3135 wakaba 1.179 $self->{ct}->{data} .= '-'; # comment
3136 wakaba 1.1 ## Stay in the state
3137    
3138 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3139     $self->{line_prev} = $self->{line};
3140     $self->{column_prev} = $self->{column};
3141     $self->{column}++;
3142 wakaba 1.179 $self->{nc}
3143 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3144     } else {
3145 wakaba 1.179 $self->{set_nc}->($self);
3146 wakaba 1.175 }
3147 wakaba 1.1
3148     redo A;
3149 wakaba 1.179 } elsif ($self->{nc} == -1) {
3150 wakaba 1.77
3151 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3152 wakaba 1.59 $self->{state} = DATA_STATE;
3153 wakaba 1.1 ## reconsume
3154    
3155 wakaba 1.179 return ($self->{ct}); # comment
3156 wakaba 1.1
3157     redo A;
3158     } else {
3159 wakaba 1.77
3160 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'dash in comment',
3161 wakaba 1.112 line => $self->{line_prev},
3162     column => $self->{column_prev});
3163 wakaba 1.179 $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
3164 wakaba 1.59 $self->{state} = COMMENT_STATE;
3165 wakaba 1.1
3166 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3167     $self->{line_prev} = $self->{line};
3168     $self->{column_prev} = $self->{column};
3169     $self->{column}++;
3170 wakaba 1.179 $self->{nc}
3171 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3172     } else {
3173 wakaba 1.179 $self->{set_nc}->($self);
3174 wakaba 1.175 }
3175 wakaba 1.1
3176     redo A;
3177     }
3178 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_STATE) {
3179 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
3180     $self->{nc} == 0x000A or # LF
3181     $self->{nc} == 0x000B or # VT
3182     $self->{nc} == 0x000C or # FF
3183     $self->{nc} == 0x0020) { # SP
3184 wakaba 1.77
3185 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
3186 wakaba 1.1
3187 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3188     $self->{line_prev} = $self->{line};
3189     $self->{column_prev} = $self->{column};
3190     $self->{column}++;
3191 wakaba 1.179 $self->{nc}
3192 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3193     } else {
3194 wakaba 1.179 $self->{set_nc}->($self);
3195 wakaba 1.175 }
3196 wakaba 1.1
3197     redo A;
3198     } else {
3199 wakaba 1.77
3200 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no space before DOCTYPE name');
3201 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
3202 wakaba 1.1 ## reconsume
3203     redo A;
3204     }
3205 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
3206 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
3207     $self->{nc} == 0x000A or # LF
3208     $self->{nc} == 0x000B or # VT
3209     $self->{nc} == 0x000C or # FF
3210     $self->{nc} == 0x0020) { # SP
3211 wakaba 1.77
3212 wakaba 1.1 ## Stay in the state
3213    
3214 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3215     $self->{line_prev} = $self->{line};
3216     $self->{column_prev} = $self->{column};
3217     $self->{column}++;
3218 wakaba 1.179 $self->{nc}
3219 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3220     } else {
3221 wakaba 1.179 $self->{set_nc}->($self);
3222 wakaba 1.175 }
3223 wakaba 1.1
3224     redo A;
3225 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3226 wakaba 1.77
3227 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE name');
3228 wakaba 1.59 $self->{state} = DATA_STATE;
3229 wakaba 1.18
3230 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3231     $self->{line_prev} = $self->{line};
3232     $self->{column_prev} = $self->{column};
3233     $self->{column}++;
3234 wakaba 1.179 $self->{nc}
3235 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3236     } else {
3237 wakaba 1.179 $self->{set_nc}->($self);
3238 wakaba 1.175 }
3239 wakaba 1.18
3240    
3241 wakaba 1.179 return ($self->{ct}); # DOCTYPE (quirks)
3242 wakaba 1.18
3243     redo A;
3244 wakaba 1.179 } elsif ($self->{nc} == -1) {
3245 wakaba 1.77
3246 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE name');
3247 wakaba 1.59 $self->{state} = DATA_STATE;
3248 wakaba 1.18 ## reconsume
3249    
3250 wakaba 1.179 return ($self->{ct}); # DOCTYPE (quirks)
3251 wakaba 1.18
3252     redo A;
3253     } else {
3254 wakaba 1.77
3255 wakaba 1.179 $self->{ct}->{name} = chr $self->{nc};
3256     delete $self->{ct}->{quirks};
3257 wakaba 1.4 ## ISSUE: "Set the token's name name to the" in the spec
3258 wakaba 1.59 $self->{state} = DOCTYPE_NAME_STATE;
3259 wakaba 1.1
3260 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3261     $self->{line_prev} = $self->{line};
3262     $self->{column_prev} = $self->{column};
3263     $self->{column}++;
3264 wakaba 1.179 $self->{nc}
3265 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3266     } else {
3267 wakaba 1.179 $self->{set_nc}->($self);
3268 wakaba 1.175 }
3269 wakaba 1.1
3270     redo A;
3271 wakaba 1.18 }
3272 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
3273 wakaba 1.18 ## ISSUE: Redundant "First," in the spec.
3274 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
3275     $self->{nc} == 0x000A or # LF
3276     $self->{nc} == 0x000B or # VT
3277     $self->{nc} == 0x000C or # FF
3278     $self->{nc} == 0x0020) { # SP
3279 wakaba 1.77
3280 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_NAME_STATE;
3281 wakaba 1.18
3282 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3283     $self->{line_prev} = $self->{line};
3284     $self->{column_prev} = $self->{column};
3285     $self->{column}++;
3286 wakaba 1.179 $self->{nc}
3287 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3288     } else {
3289 wakaba 1.179 $self->{set_nc}->($self);
3290 wakaba 1.175 }
3291 wakaba 1.18
3292     redo A;
3293 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3294 wakaba 1.77
3295 wakaba 1.59 $self->{state} = DATA_STATE;
3296 wakaba 1.1
3297 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3298     $self->{line_prev} = $self->{line};
3299     $self->{column_prev} = $self->{column};
3300     $self->{column}++;
3301 wakaba 1.179 $self->{nc}
3302 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3303     } else {
3304 wakaba 1.179 $self->{set_nc}->($self);
3305 wakaba 1.175 }
3306 wakaba 1.1
3307    
3308 wakaba 1.179 return ($self->{ct}); # DOCTYPE
3309 wakaba 1.1
3310     redo A;
3311 wakaba 1.179 } elsif ($self->{nc} == -1) {
3312 wakaba 1.77
3313 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3314 wakaba 1.59 $self->{state} = DATA_STATE;
3315 wakaba 1.1 ## reconsume
3316    
3317 wakaba 1.179 $self->{ct}->{quirks} = 1;
3318     return ($self->{ct}); # DOCTYPE
3319 wakaba 1.1
3320     redo A;
3321     } else {
3322 wakaba 1.77
3323 wakaba 1.179 $self->{ct}->{name}
3324     .= chr ($self->{nc}); # DOCTYPE
3325 wakaba 1.18 ## Stay in the state
3326 wakaba 1.1
3327 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3328     $self->{line_prev} = $self->{line};
3329     $self->{column_prev} = $self->{column};
3330     $self->{column}++;
3331 wakaba 1.179 $self->{nc}
3332 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3333     } else {
3334 wakaba 1.179 $self->{set_nc}->($self);
3335 wakaba 1.175 }
3336 wakaba 1.1
3337     redo A;
3338     }
3339 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
3340 wakaba 1.179 if ($self->{nc} == 0x0009 or # HT
3341     $self->{nc} == 0x000A or # LF
3342     $self->{nc} == 0x000B or # VT
3343     $self->{nc} == 0x000C or # FF
3344     $self->{nc} == 0x0020) { # SP
3345 wakaba 1.77
3346 wakaba 1.18 ## Stay in the state
3347 wakaba 1.1
3348 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3349     $self->{line_prev} = $self->{line};
3350     $self->{column_prev} = $self->{column};
3351     $self->{column}++;
3352 wakaba 1.179 $self->{nc}
3353 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3354     } else {
3355 wakaba 1.179 $self->{set_nc}->($self);
3356 wakaba 1.175 }
3357 wakaba 1.1
3358     redo A;
3359 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3360 wakaba 1.77
3361 wakaba 1.59 $self->{state} = DATA_STATE;
3362 wakaba 1.1
3363 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3364     $self->{line_prev} = $self->{line};
3365     $self->{column_prev} = $self->{column};
3366     $self->{column}++;
3367 wakaba 1.179 $self->{nc}
3368 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3369     } else {
3370 wakaba 1.179 $self->{set_nc}->($self);
3371 wakaba 1.175 }
3372 wakaba 1.1
3373    
3374 wakaba 1.179 return ($self->{ct}); # DOCTYPE
3375 wakaba 1.1
3376     redo A;
3377 wakaba 1.179 } elsif ($self->{nc} == -1) {
3378 wakaba 1.77
3379 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3380 wakaba 1.59 $self->{state} = DATA_STATE;
3381 wakaba 1.18 ## reconsume
3382    
3383 wakaba 1.179 $self->{ct}->{quirks} = 1;
3384     return ($self->{ct}); # DOCTYPE
3385 wakaba 1.18
3386     redo A;
3387 wakaba 1.179 } elsif ($self->{nc} == 0x0050 or # P
3388     $self->{nc} == 0x0070) { # p
3389 wakaba 1.162 $self->{state} = PUBLIC_STATE;
3390 wakaba 1.179 $self->{s_kwd} = chr $self->{nc};
3391 wakaba 1.18
3392 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3393     $self->{line_prev} = $self->{line};
3394     $self->{column_prev} = $self->{column};
3395     $self->{column}++;
3396 wakaba 1.179 $self->{nc}
3397 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3398     } else {
3399 wakaba 1.179 $self->{set_nc}->($self);
3400 wakaba 1.175 }
3401 wakaba 1.18
3402 wakaba 1.162 redo A;
3403 wakaba 1.179 } elsif ($self->{nc} == 0x0053 or # S
3404     $self->{nc} == 0x0073) { # s
3405 wakaba 1.162 $self->{state} = SYSTEM_STATE;
3406 wakaba 1.179 $self->{s_kwd} = chr $self->{nc};
3407 wakaba 1.162
3408 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3409     $self->{line_prev} = $self->{line};
3410     $self->{column_prev} = $self->{column};
3411     $self->{column}++;
3412 wakaba 1.179 $self->{nc}
3413 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3414     } else {
3415 wakaba 1.179 $self->{set_nc}->($self);
3416 wakaba 1.175 }
3417 wakaba 1.18
3418 wakaba 1.162 redo A;
3419 wakaba 1.18 } else {
3420 wakaba 1.162
3421     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name');
3422 wakaba 1.179 $self->{ct}->{quirks} = 1;
3423 wakaba 1.18
3424 wakaba 1.162 $self->{state} = BOGUS_DOCTYPE_STATE;
3425 wakaba 1.18
3426 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3427     $self->{line_prev} = $self->{line};
3428     $self->{column_prev} = $self->{column};
3429     $self->{column}++;
3430 wakaba 1.179 $self->{nc}
3431 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3432     } else {
3433 wakaba 1.179 $self->{set_nc}->($self);
3434 wakaba 1.175 }
3435 wakaba 1.18
3436 wakaba 1.162 redo A;
3437 wakaba 1.18 }
3438 wakaba 1.162 } elsif ($self->{state} == PUBLIC_STATE) {
3439     ## ASCII case-insensitive
3440 wakaba 1.179 if ($self->{nc} == [
3441 wakaba 1.162 undef,
3442     0x0055, # U
3443     0x0042, # B
3444     0x004C, # L
3445     0x0049, # I
3446 wakaba 1.179 ]->[length $self->{s_kwd}] or
3447     $self->{nc} == [
3448 wakaba 1.162 undef,
3449     0x0075, # u
3450     0x0062, # b
3451     0x006C, # l
3452     0x0069, # i
3453 wakaba 1.179 ]->[length $self->{s_kwd}]) {
3454 wakaba 1.162
3455     ## Stay in the state.
3456 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
3457 wakaba 1.162
3458 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3459     $self->{line_prev} = $self->{line};
3460     $self->{column_prev} = $self->{column};
3461     $self->{column}++;
3462 wakaba 1.179 $self->{nc}
3463 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3464     } else {
3465 wakaba 1.179 $self->{set_nc}->($self);
3466 wakaba 1.175 }
3467 wakaba 1.18
3468 wakaba 1.162 redo A;
3469 wakaba 1.179 } elsif ((length $self->{s_kwd}) == 5 and
3470     ($self->{nc} == 0x0043 or # C
3471     $self->{nc} == 0x0063)) { # c
3472 wakaba 1.162
3473     $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3474    
3475 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3476     $self->{line_prev} = $self->{line};
3477     $self->{column_prev} = $self->{column};
3478     $self->{column}++;
3479 wakaba 1.179 $self->{nc}
3480 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3481     } else {
3482 wakaba 1.179 $self->{set_nc}->($self);
3483 wakaba 1.175 }
3484 wakaba 1.18
3485 wakaba 1.162 redo A;
3486 wakaba 1.18 } else {
3487 wakaba 1.162
3488     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name',
3489     line => $self->{line_prev},
3490 wakaba 1.179 column => $self->{column_prev} + 1 - length $self->{s_kwd});
3491     $self->{ct}->{quirks} = 1;
3492 wakaba 1.162
3493     $self->{state} = BOGUS_DOCTYPE_STATE;
3494     ## Reconsume.
3495     redo A;
3496 wakaba 1.18 }
3497 wakaba 1.162 } elsif ($self->{state} == SYSTEM_STATE) {
3498     ## ASCII case-insensitive
3499 wakaba 1.179 if ($self->{nc} == [
3500 wakaba 1.162 undef,
3501     0x0059, # Y
3502     0x0053, # S
3503     0x0054, # T
3504     0x0045, # E
3505 wakaba 1.179 ]->[length $self->{s_kwd}] or
3506     $self->{nc} == [
3507 wakaba 1.162 undef,
3508     0x0079, # y
3509     0x0073, # s
3510     0x0074, # t
3511     0x0065, # e
3512 wakaba 1.179 ]->[length $self->{s_kwd}]) {
3513 wakaba 1.162
3514     ## Stay in the state.
3515 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
3516 wakaba 1.162
3517 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3518     $self->{line_prev} = $self->{line};
3519     $self->{column_prev} = $self->{column};
3520     $self->{column}++;
3521 wakaba 1.179 $self->{nc}
3522 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3523     } else {
3524 wakaba 1.179 $self->{set_nc}->($self);
3525 wakaba 1.175 }
3526 wakaba 1.18
3527 wakaba 1.162 redo A;
3528 wakaba 1.179 } elsif ((length $self->{s_kwd}) == 5 and
3529     ($self->{nc} == 0x004D or # M
3530     $self->{nc} == 0x006D)) { # m
3531 wakaba 1.18
3532 wakaba 1.162 $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
3533 wakaba 1.77
3534 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3535     $self->{line_prev} = $self->{line};
3536     $self->{column_prev} = $self->{column};
3537     $self->{column}++;
3538 wakaba 1.179 $self->{nc}
3539 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3540     } else {
3541 wakaba 1.179 $self->{set_nc}->($self);
3542 wakaba 1.175 }
3543 wakaba 1.18
3544 wakaba 1.162 redo A;
3545     } else {
3546    
3547     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name',
3548     line => $self->{line_prev},
3549 wakaba 1.179 column => $self->{column_prev} + 1 - length $self->{s_kwd});
3550     $self->{ct}->{quirks} = 1;
3551 wakaba 1.162
3552     $self->{state} = BOGUS_DOCTYPE_STATE;
3553     ## Reconsume.
3554     redo A;
3555 wakaba 1.18 }
3556 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
3557 wakaba 1.18 if ({
3558     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
3559     #0x000D => 1, # HT, LF, VT, FF, SP, CR
3560 wakaba 1.179 }->{$self->{nc}}) {
3561 wakaba 1.77
3562 wakaba 1.1 ## Stay in the state
3563    
3564 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3565     $self->{line_prev} = $self->{line};
3566     $self->{column_prev} = $self->{column};
3567     $self->{column}++;
3568 wakaba 1.179 $self->{nc}
3569 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3570     } else {
3571 wakaba 1.179 $self->{set_nc}->($self);
3572 wakaba 1.175 }
3573 wakaba 1.1
3574     redo A;
3575 wakaba 1.179 } elsif ($self->{nc} eq 0x0022) { # "
3576 wakaba 1.77
3577 wakaba 1.179 $self->{ct}->{pubid} = ''; # DOCTYPE
3578 wakaba 1.59 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
3579 wakaba 1.18
3580 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3581     $self->{line_prev} = $self->{line};
3582     $self->{column_prev} = $self->{column};
3583     $self->{column}++;
3584 wakaba 1.179 $self->{nc}
3585 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3586     } else {
3587 wakaba 1.179 $self->{set_nc}->($self);
3588 wakaba 1.175 }
3589 wakaba 1.18
3590     redo A;
3591 wakaba 1.179 } elsif ($self->{nc} eq 0x0027) { # '
3592 wakaba 1.77
3593 wakaba 1.179 $self->{ct}->{pubid} = ''; # DOCTYPE
3594 wakaba 1.59 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
3595 wakaba 1.18
3596 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3597     $self->{line_prev} = $self->{line};
3598     $self->{column_prev} = $self->{column};
3599     $self->{column}++;
3600 wakaba 1.179 $self->{nc}
3601 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3602     } else {
3603 wakaba 1.179 $self->{set_nc}->($self);
3604 wakaba 1.175 }
3605 wakaba 1.18
3606     redo A;
3607 wakaba 1.179 } elsif ($self->{nc} eq 0x003E) { # >
3608 wakaba 1.77
3609 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no PUBLIC literal');
3610 wakaba 1.18
3611 wakaba 1.59 $self->{state} = DATA_STATE;
3612 wakaba 1.18
3613 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3614     $self->{line_prev} = $self->{line};
3615     $self->{column_prev} = $self->{column};
3616     $self->{column}++;
3617 wakaba 1.179 $self->{nc}
3618 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3619     } else {
3620 wakaba 1.179 $self->{set_nc}->($self);
3621 wakaba 1.175 }
3622 wakaba 1.18
3623    
3624 wakaba 1.179 $self->{ct}->{quirks} = 1;
3625     return ($self->{ct}); # DOCTYPE
3626 wakaba 1.18
3627     redo A;
3628 wakaba 1.179 } elsif ($self->{nc} == -1) {
3629 wakaba 1.77
3630 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3631 wakaba 1.18
3632 wakaba 1.59 $self->{state} = DATA_STATE;
3633 wakaba 1.1 ## reconsume
3634    
3635 wakaba 1.179 $self->{ct}->{quirks} = 1;
3636     return ($self->{ct}); # DOCTYPE
3637 wakaba 1.1
3638     redo A;
3639     } else {
3640 wakaba 1.77
3641 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after PUBLIC');
3642 wakaba 1.179 $self->{ct}->{quirks} = 1;
3643 wakaba 1.73
3644 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3645 wakaba 1.18
3646 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3647     $self->{line_prev} = $self->{line};
3648     $self->{column_prev} = $self->{column};
3649     $self->{column}++;
3650 wakaba 1.179 $self->{nc}
3651 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3652     } else {
3653 wakaba 1.179 $self->{set_nc}->($self);
3654 wakaba 1.175 }
3655 wakaba 1.18
3656     redo A;
3657     }
3658 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
3659 wakaba 1.179 if ($self->{nc} == 0x0022) { # "
3660 wakaba 1.77
3661 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3662 wakaba 1.18
3663 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3664     $self->{line_prev} = $self->{line};
3665     $self->{column_prev} = $self->{column};
3666     $self->{column}++;
3667 wakaba 1.179 $self->{nc}
3668 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3669     } else {
3670 wakaba 1.179 $self->{set_nc}->($self);
3671 wakaba 1.175 }
3672 wakaba 1.18
3673     redo A;
3674 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3675 wakaba 1.77
3676 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3677 wakaba 1.70
3678     $self->{state} = DATA_STATE;
3679    
3680 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3681     $self->{line_prev} = $self->{line};
3682     $self->{column_prev} = $self->{column};
3683     $self->{column}++;
3684 wakaba 1.179 $self->{nc}
3685 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3686     } else {
3687 wakaba 1.179 $self->{set_nc}->($self);
3688 wakaba 1.175 }
3689 wakaba 1.70
3690    
3691 wakaba 1.179 $self->{ct}->{quirks} = 1;
3692     return ($self->{ct}); # DOCTYPE
3693 wakaba 1.70
3694     redo A;
3695 wakaba 1.179 } elsif ($self->{nc} == -1) {
3696 wakaba 1.77
3697 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3698 wakaba 1.18
3699 wakaba 1.59 $self->{state} = DATA_STATE;
3700 wakaba 1.18 ## reconsume
3701    
3702 wakaba 1.179 $self->{ct}->{quirks} = 1;
3703     return ($self->{ct}); # DOCTYPE
3704 wakaba 1.18
3705     redo A;
3706     } else {
3707 wakaba 1.77
3708 wakaba 1.179 $self->{ct}->{pubid} # DOCTYPE
3709     .= chr $self->{nc};
3710     $self->{read_until}->($self->{ct}->{pubid}, q[">],
3711     length $self->{ct}->{pubid});
3712 wakaba 1.169
3713 wakaba 1.18 ## Stay in the state
3714    
3715 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3716     $self->{line_prev} = $self->{line};
3717     $self->{column_prev} = $self->{column};
3718     $self->{column}++;
3719 wakaba 1.179 $self->{nc}
3720 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3721     } else {
3722 wakaba 1.179 $self->{set_nc}->($self);
3723 wakaba 1.175 }
3724 wakaba 1.18
3725     redo A;
3726     }
3727 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
3728 wakaba 1.179 if ($self->{nc} == 0x0027) { # '
3729 wakaba 1.77
3730 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3731 wakaba 1.18
3732 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3733     $self->{line_prev} = $self->{line};
3734     $self->{column_prev} = $self->{column};
3735     $self->{column}++;
3736 wakaba 1.179 $self->{nc}
3737 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3738     } else {
3739 wakaba 1.179 $self->{set_nc}->($self);
3740 wakaba 1.175 }
3741 wakaba 1.18
3742     redo A;
3743 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3744 wakaba 1.77
3745 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3746 wakaba 1.70
3747     $self->{state} = DATA_STATE;
3748    
3749 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3750     $self->{line_prev} = $self->{line};
3751     $self->{column_prev} = $self->{column};
3752     $self->{column}++;
3753 wakaba 1.179 $self->{nc}
3754 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3755     } else {
3756 wakaba 1.179 $self->{set_nc}->($self);
3757 wakaba 1.175 }
3758 wakaba 1.70
3759    
3760 wakaba 1.179 $self->{ct}->{quirks} = 1;
3761     return ($self->{ct}); # DOCTYPE
3762 wakaba 1.70
3763     redo A;
3764 wakaba 1.179 } elsif ($self->{nc} == -1) {
3765 wakaba 1.77
3766 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3767 wakaba 1.18
3768 wakaba 1.59 $self->{state} = DATA_STATE;
3769 wakaba 1.18 ## reconsume
3770    
3771 wakaba 1.179 $self->{ct}->{quirks} = 1;
3772     return ($self->{ct}); # DOCTYPE
3773 wakaba 1.18
3774     redo A;
3775     } else {
3776 wakaba 1.77
3777 wakaba 1.179 $self->{ct}->{pubid} # DOCTYPE
3778     .= chr $self->{nc};
3779     $self->{read_until}->($self->{ct}->{pubid}, q['>],
3780     length $self->{ct}->{pubid});
3781 wakaba 1.169
3782 wakaba 1.18 ## Stay in the state
3783    
3784 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3785     $self->{line_prev} = $self->{line};
3786     $self->{column_prev} = $self->{column};
3787     $self->{column}++;
3788 wakaba 1.179 $self->{nc}
3789 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3790     } else {
3791 wakaba 1.179 $self->{set_nc}->($self);
3792 wakaba 1.175 }
3793 wakaba 1.18
3794     redo A;
3795     }
3796 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
3797 wakaba 1.18 if ({
3798     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
3799     #0x000D => 1, # HT, LF, VT, FF, SP, CR
3800 wakaba 1.179 }->{$self->{nc}}) {
3801 wakaba 1.77
3802 wakaba 1.1 ## Stay in the state
3803    
3804 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3805     $self->{line_prev} = $self->{line};
3806     $self->{column_prev} = $self->{column};
3807     $self->{column}++;
3808 wakaba 1.179 $self->{nc}
3809 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3810     } else {
3811 wakaba 1.179 $self->{set_nc}->($self);
3812 wakaba 1.175 }
3813 wakaba 1.1
3814     redo A;
3815 wakaba 1.179 } elsif ($self->{nc} == 0x0022) { # "
3816 wakaba 1.77
3817 wakaba 1.179 $self->{ct}->{sysid} = ''; # DOCTYPE
3818 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
3819 wakaba 1.18
3820 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3821     $self->{line_prev} = $self->{line};
3822     $self->{column_prev} = $self->{column};
3823     $self->{column}++;
3824 wakaba 1.179 $self->{nc}
3825 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3826     } else {
3827 wakaba 1.179 $self->{set_nc}->($self);
3828 wakaba 1.175 }
3829 wakaba 1.18
3830     redo A;
3831 wakaba 1.179 } elsif ($self->{nc} == 0x0027) { # '
3832 wakaba 1.77
3833 wakaba 1.179 $self->{ct}->{sysid} = ''; # DOCTYPE
3834 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
3835 wakaba 1.18
3836 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3837     $self->{line_prev} = $self->{line};
3838     $self->{column_prev} = $self->{column};
3839     $self->{column}++;
3840 wakaba 1.179 $self->{nc}
3841 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3842     } else {
3843 wakaba 1.179 $self->{set_nc}->($self);
3844 wakaba 1.175 }
3845 wakaba 1.18
3846     redo A;
3847 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3848 wakaba 1.77
3849 wakaba 1.59 $self->{state} = DATA_STATE;
3850 wakaba 1.18
3851 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3852     $self->{line_prev} = $self->{line};
3853     $self->{column_prev} = $self->{column};
3854     $self->{column}++;
3855 wakaba 1.179 $self->{nc}
3856 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3857     } else {
3858 wakaba 1.179 $self->{set_nc}->($self);
3859 wakaba 1.175 }
3860 wakaba 1.18
3861    
3862 wakaba 1.179 return ($self->{ct}); # DOCTYPE
3863 wakaba 1.18
3864     redo A;
3865 wakaba 1.179 } elsif ($self->{nc} == -1) {
3866 wakaba 1.77
3867 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3868 wakaba 1.18
3869 wakaba 1.59 $self->{state} = DATA_STATE;
3870 wakaba 1.26 ## reconsume
3871 wakaba 1.18
3872 wakaba 1.179 $self->{ct}->{quirks} = 1;
3873     return ($self->{ct}); # DOCTYPE
3874 wakaba 1.18
3875     redo A;
3876     } else {
3877 wakaba 1.77
3878 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after PUBLIC literal');
3879 wakaba 1.179 $self->{ct}->{quirks} = 1;
3880 wakaba 1.73
3881 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3882 wakaba 1.18
3883 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3884     $self->{line_prev} = $self->{line};
3885     $self->{column_prev} = $self->{column};
3886     $self->{column}++;
3887 wakaba 1.179 $self->{nc}
3888 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3889     } else {
3890 wakaba 1.179 $self->{set_nc}->($self);
3891 wakaba 1.175 }
3892 wakaba 1.18
3893     redo A;
3894 wakaba 1.1 }
3895 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
3896 wakaba 1.18 if ({
3897     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
3898     #0x000D => 1, # HT, LF, VT, FF, SP, CR
3899 wakaba 1.179 }->{$self->{nc}}) {
3900 wakaba 1.77
3901 wakaba 1.1 ## Stay in the state
3902    
3903 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3904     $self->{line_prev} = $self->{line};
3905     $self->{column_prev} = $self->{column};
3906     $self->{column}++;
3907 wakaba 1.179 $self->{nc}
3908 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3909     } else {
3910 wakaba 1.179 $self->{set_nc}->($self);
3911 wakaba 1.175 }
3912 wakaba 1.1
3913     redo A;
3914 wakaba 1.179 } elsif ($self->{nc} == 0x0022) { # "
3915 wakaba 1.77
3916 wakaba 1.179 $self->{ct}->{sysid} = ''; # DOCTYPE
3917 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
3918 wakaba 1.18
3919 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3920     $self->{line_prev} = $self->{line};
3921     $self->{column_prev} = $self->{column};
3922     $self->{column}++;
3923 wakaba 1.179 $self->{nc}
3924 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3925     } else {
3926 wakaba 1.179 $self->{set_nc}->($self);
3927 wakaba 1.175 }
3928 wakaba 1.18
3929     redo A;
3930 wakaba 1.179 } elsif ($self->{nc} == 0x0027) { # '
3931 wakaba 1.77
3932 wakaba 1.179 $self->{ct}->{sysid} = ''; # DOCTYPE
3933 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
3934 wakaba 1.18
3935 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3936     $self->{line_prev} = $self->{line};
3937     $self->{column_prev} = $self->{column};
3938     $self->{column}++;
3939 wakaba 1.179 $self->{nc}
3940 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3941     } else {
3942 wakaba 1.179 $self->{set_nc}->($self);
3943 wakaba 1.175 }
3944 wakaba 1.18
3945     redo A;
3946 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3947 wakaba 1.77
3948 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no SYSTEM literal');
3949 wakaba 1.59 $self->{state} = DATA_STATE;
3950 wakaba 1.1
3951 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3952     $self->{line_prev} = $self->{line};
3953     $self->{column_prev} = $self->{column};
3954     $self->{column}++;
3955 wakaba 1.179 $self->{nc}
3956 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3957     } else {
3958 wakaba 1.179 $self->{set_nc}->($self);
3959 wakaba 1.175 }
3960 wakaba 1.1
3961    
3962 wakaba 1.179 $self->{ct}->{quirks} = 1;
3963     return ($self->{ct}); # DOCTYPE
3964 wakaba 1.1
3965     redo A;
3966 wakaba 1.179 } elsif ($self->{nc} == -1) {
3967 wakaba 1.77
3968 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3969 wakaba 1.18
3970 wakaba 1.59 $self->{state} = DATA_STATE;
3971 wakaba 1.26 ## reconsume
3972 wakaba 1.18
3973 wakaba 1.179 $self->{ct}->{quirks} = 1;
3974     return ($self->{ct}); # DOCTYPE
3975 wakaba 1.18
3976     redo A;
3977     } else {
3978 wakaba 1.77
3979 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after SYSTEM');
3980 wakaba 1.179 $self->{ct}->{quirks} = 1;
3981 wakaba 1.73
3982 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3983 wakaba 1.18
3984 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3985     $self->{line_prev} = $self->{line};
3986     $self->{column_prev} = $self->{column};
3987     $self->{column}++;
3988 wakaba 1.179 $self->{nc}
3989 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3990     } else {
3991 wakaba 1.179 $self->{set_nc}->($self);
3992 wakaba 1.175 }
3993 wakaba 1.18
3994     redo A;
3995     }
3996 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
3997 wakaba 1.179 if ($self->{nc} == 0x0022) { # "
3998 wakaba 1.77
3999 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
4000 wakaba 1.18
4001 wakaba 1.175 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 wakaba 1.179 $self->{nc}
4006 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4007     } else {
4008 wakaba 1.179 $self->{set_nc}->($self);
4009 wakaba 1.175 }
4010 wakaba 1.18
4011     redo A;
4012 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
4013 wakaba 1.77
4014 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4015 wakaba 1.70
4016     $self->{state} = DATA_STATE;
4017    
4018 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4019     $self->{line_prev} = $self->{line};
4020     $self->{column_prev} = $self->{column};
4021     $self->{column}++;
4022 wakaba 1.179 $self->{nc}
4023 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4024     } else {
4025 wakaba 1.179 $self->{set_nc}->($self);
4026 wakaba 1.175 }
4027 wakaba 1.70
4028    
4029 wakaba 1.179 $self->{ct}->{quirks} = 1;
4030     return ($self->{ct}); # DOCTYPE
4031 wakaba 1.70
4032     redo A;
4033 wakaba 1.179 } elsif ($self->{nc} == -1) {
4034 wakaba 1.77
4035 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4036 wakaba 1.18
4037 wakaba 1.59 $self->{state} = DATA_STATE;
4038 wakaba 1.1 ## reconsume
4039    
4040 wakaba 1.179 $self->{ct}->{quirks} = 1;
4041     return ($self->{ct}); # DOCTYPE
4042 wakaba 1.1
4043     redo A;
4044     } else {
4045 wakaba 1.77
4046 wakaba 1.179 $self->{ct}->{sysid} # DOCTYPE
4047     .= chr $self->{nc};
4048     $self->{read_until}->($self->{ct}->{sysid}, q[">],
4049     length $self->{ct}->{sysid});
4050 wakaba 1.169
4051 wakaba 1.18 ## Stay in the state
4052    
4053 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4054     $self->{line_prev} = $self->{line};
4055     $self->{column_prev} = $self->{column};
4056     $self->{column}++;
4057 wakaba 1.179 $self->{nc}
4058 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4059     } else {
4060 wakaba 1.179 $self->{set_nc}->($self);
4061 wakaba 1.175 }
4062 wakaba 1.18
4063     redo A;
4064     }
4065 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
4066 wakaba 1.179 if ($self->{nc} == 0x0027) { # '
4067 wakaba 1.77
4068 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
4069 wakaba 1.18
4070 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4071     $self->{line_prev} = $self->{line};
4072     $self->{column_prev} = $self->{column};
4073     $self->{column}++;
4074 wakaba 1.179 $self->{nc}
4075 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4076     } else {
4077 wakaba 1.179 $self->{set_nc}->($self);
4078 wakaba 1.175 }
4079 wakaba 1.18
4080     redo A;
4081 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
4082 wakaba 1.77
4083 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4084 wakaba 1.70
4085     $self->{state} = DATA_STATE;
4086    
4087 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4088     $self->{line_prev} = $self->{line};
4089     $self->{column_prev} = $self->{column};
4090     $self->{column}++;
4091 wakaba 1.179 $self->{nc}
4092 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4093     } else {
4094 wakaba 1.179 $self->{set_nc}->($self);
4095 wakaba 1.175 }
4096 wakaba 1.70
4097    
4098 wakaba 1.179 $self->{ct}->{quirks} = 1;
4099     return ($self->{ct}); # DOCTYPE
4100 wakaba 1.70
4101     redo A;
4102 wakaba 1.179 } elsif ($self->{nc} == -1) {
4103 wakaba 1.77
4104 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4105 wakaba 1.18
4106 wakaba 1.59 $self->{state} = DATA_STATE;
4107 wakaba 1.18 ## reconsume
4108    
4109 wakaba 1.179 $self->{ct}->{quirks} = 1;
4110     return ($self->{ct}); # DOCTYPE
4111 wakaba 1.18
4112     redo A;
4113     } else {
4114 wakaba 1.77
4115 wakaba 1.179 $self->{ct}->{sysid} # DOCTYPE
4116     .= chr $self->{nc};
4117     $self->{read_until}->($self->{ct}->{sysid}, q['>],
4118     length $self->{ct}->{sysid});
4119 wakaba 1.169
4120 wakaba 1.18 ## Stay in the state
4121    
4122 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4123     $self->{line_prev} = $self->{line};
4124     $self->{column_prev} = $self->{column};
4125     $self->{column}++;
4126 wakaba 1.179 $self->{nc}
4127 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4128     } else {
4129 wakaba 1.179 $self->{set_nc}->($self);
4130 wakaba 1.175 }
4131 wakaba 1.18
4132     redo A;
4133     }
4134 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
4135 wakaba 1.18 if ({
4136     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
4137     #0x000D => 1, # HT, LF, VT, FF, SP, CR
4138 wakaba 1.179 }->{$self->{nc}}) {
4139 wakaba 1.77
4140 wakaba 1.18 ## Stay in the state
4141    
4142 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4143     $self->{line_prev} = $self->{line};
4144     $self->{column_prev} = $self->{column};
4145     $self->{column}++;
4146 wakaba 1.179 $self->{nc}
4147 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4148     } else {
4149 wakaba 1.179 $self->{set_nc}->($self);
4150 wakaba 1.175 }
4151 wakaba 1.18
4152     redo A;
4153 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
4154 wakaba 1.77
4155 wakaba 1.59 $self->{state} = DATA_STATE;
4156 wakaba 1.18
4157 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4158     $self->{line_prev} = $self->{line};
4159     $self->{column_prev} = $self->{column};
4160     $self->{column}++;
4161 wakaba 1.179 $self->{nc}
4162 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4163     } else {
4164 wakaba 1.179 $self->{set_nc}->($self);
4165 wakaba 1.175 }
4166 wakaba 1.18
4167    
4168 wakaba 1.179 return ($self->{ct}); # DOCTYPE
4169 wakaba 1.18
4170     redo A;
4171 wakaba 1.179 } elsif ($self->{nc} == -1) {
4172 wakaba 1.77
4173 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
4174 wakaba 1.59 $self->{state} = DATA_STATE;
4175 wakaba 1.26 ## reconsume
4176 wakaba 1.18
4177 wakaba 1.179 $self->{ct}->{quirks} = 1;
4178     return ($self->{ct}); # DOCTYPE
4179 wakaba 1.18
4180     redo A;
4181     } else {
4182 wakaba 1.77
4183 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after SYSTEM literal');
4184 wakaba 1.179 #$self->{ct}->{quirks} = 1;
4185 wakaba 1.73
4186 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
4187 wakaba 1.1
4188 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4189     $self->{line_prev} = $self->{line};
4190     $self->{column_prev} = $self->{column};
4191     $self->{column}++;
4192 wakaba 1.179 $self->{nc}
4193 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4194     } else {
4195 wakaba 1.179 $self->{set_nc}->($self);
4196 wakaba 1.175 }
4197 wakaba 1.1
4198     redo A;
4199     }
4200 wakaba 1.59 } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
4201 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
4202 wakaba 1.77
4203 wakaba 1.59 $self->{state} = DATA_STATE;
4204 wakaba 1.1
4205 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4206     $self->{line_prev} = $self->{line};
4207     $self->{column_prev} = $self->{column};
4208     $self->{column}++;
4209 wakaba 1.179 $self->{nc}
4210 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4211     } else {
4212 wakaba 1.179 $self->{set_nc}->($self);
4213 wakaba 1.175 }
4214 wakaba 1.1
4215    
4216 wakaba 1.179 return ($self->{ct}); # DOCTYPE
4217 wakaba 1.1
4218     redo A;
4219 wakaba 1.179 } elsif ($self->{nc} == -1) {
4220 wakaba 1.77
4221 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
4222 wakaba 1.59 $self->{state} = DATA_STATE;
4223 wakaba 1.1 ## reconsume
4224    
4225 wakaba 1.179 return ($self->{ct}); # DOCTYPE
4226 wakaba 1.1
4227     redo A;
4228     } else {
4229 wakaba 1.77
4230 wakaba 1.169 my $s = '';
4231     $self->{read_until}->($s, q[>], 0);
4232    
4233 wakaba 1.1 ## Stay in the state
4234    
4235 wakaba 1.175 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 wakaba 1.179 $self->{nc}
4240 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4241     } else {
4242 wakaba 1.179 $self->{set_nc}->($self);
4243 wakaba 1.175 }
4244 wakaba 1.1
4245     redo A;
4246     }
4247 wakaba 1.161 } elsif ($self->{state} == CDATA_SECTION_STATE) {
4248     ## NOTE: "CDATA section state" in the state is jointly implemented
4249     ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
4250     ## and |CDATA_SECTION_MSE2_STATE|.
4251 wakaba 1.124
4252 wakaba 1.179 if ($self->{nc} == 0x005D) { # ]
4253 wakaba 1.161
4254     $self->{state} = CDATA_SECTION_MSE1_STATE;
4255    
4256 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4257     $self->{line_prev} = $self->{line};
4258     $self->{column_prev} = $self->{column};
4259     $self->{column}++;
4260 wakaba 1.179 $self->{nc}
4261 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4262     } else {
4263 wakaba 1.179 $self->{set_nc}->($self);
4264 wakaba 1.175 }
4265 wakaba 1.161
4266     redo A;
4267 wakaba 1.179 } elsif ($self->{nc} == -1) {
4268 wakaba 1.161 $self->{state} = DATA_STATE;
4269    
4270 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4271     $self->{line_prev} = $self->{line};
4272     $self->{column_prev} = $self->{column};
4273     $self->{column}++;
4274 wakaba 1.179 $self->{nc}
4275 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4276     } else {
4277 wakaba 1.179 $self->{set_nc}->($self);
4278 wakaba 1.175 }
4279 wakaba 1.124
4280 wakaba 1.179 if (length $self->{ct}->{data}) { # character
4281 wakaba 1.161
4282 wakaba 1.179 return ($self->{ct}); # character
4283 wakaba 1.161 } else {
4284    
4285 wakaba 1.179 ## No token to emit. $self->{ct} is discarded.
4286 wakaba 1.161 }
4287     redo A;
4288     } else {
4289    
4290 wakaba 1.179 $self->{ct}->{data} .= chr $self->{nc};
4291     $self->{read_until}->($self->{ct}->{data},
4292 wakaba 1.169 q<]>,
4293 wakaba 1.179 length $self->{ct}->{data});
4294 wakaba 1.169
4295 wakaba 1.161 ## Stay in the state.
4296    
4297 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4298     $self->{line_prev} = $self->{line};
4299     $self->{column_prev} = $self->{column};
4300     $self->{column}++;
4301 wakaba 1.179 $self->{nc}
4302 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4303     } else {
4304 wakaba 1.179 $self->{set_nc}->($self);
4305 wakaba 1.175 }
4306 wakaba 1.124
4307 wakaba 1.161 redo A;
4308     }
4309    
4310     ## ISSUE: "text tokens" in spec.
4311     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
4312 wakaba 1.179 if ($self->{nc} == 0x005D) { # ]
4313 wakaba 1.161
4314     $self->{state} = CDATA_SECTION_MSE2_STATE;
4315    
4316 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4317     $self->{line_prev} = $self->{line};
4318     $self->{column_prev} = $self->{column};
4319     $self->{column}++;
4320 wakaba 1.179 $self->{nc}
4321 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4322     } else {
4323 wakaba 1.179 $self->{set_nc}->($self);
4324 wakaba 1.175 }
4325 wakaba 1.124
4326 wakaba 1.161 redo A;
4327     } else {
4328    
4329 wakaba 1.179 $self->{ct}->{data} .= ']';
4330 wakaba 1.161 $self->{state} = CDATA_SECTION_STATE;
4331     ## Reconsume.
4332     redo A;
4333     }
4334     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
4335 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
4336 wakaba 1.161 $self->{state} = DATA_STATE;
4337    
4338 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4339     $self->{line_prev} = $self->{line};
4340     $self->{column_prev} = $self->{column};
4341     $self->{column}++;
4342 wakaba 1.179 $self->{nc}
4343 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4344     } else {
4345 wakaba 1.179 $self->{set_nc}->($self);
4346 wakaba 1.175 }
4347 wakaba 1.124
4348 wakaba 1.179 if (length $self->{ct}->{data}) { # character
4349 wakaba 1.161
4350 wakaba 1.179 return ($self->{ct}); # character
4351 wakaba 1.124 } else {
4352    
4353 wakaba 1.179 ## No token to emit. $self->{ct} is discarded.
4354 wakaba 1.124 }
4355 wakaba 1.161 redo A;
4356 wakaba 1.179 } elsif ($self->{nc} == 0x005D) { # ]
4357 wakaba 1.161 # character
4358 wakaba 1.179 $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
4359 wakaba 1.161 ## Stay in the state.
4360 wakaba 1.124
4361 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4362     $self->{line_prev} = $self->{line};
4363     $self->{column_prev} = $self->{column};
4364     $self->{column}++;
4365 wakaba 1.179 $self->{nc}
4366 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4367     } else {
4368 wakaba 1.179 $self->{set_nc}->($self);
4369 wakaba 1.175 }
4370 wakaba 1.124
4371 wakaba 1.161 redo A;
4372 wakaba 1.124 } else {
4373    
4374 wakaba 1.179 $self->{ct}->{data} .= ']]'; # character
4375 wakaba 1.161 $self->{state} = CDATA_SECTION_STATE;
4376     ## Reconsume.
4377     redo A;
4378 wakaba 1.124 }
4379 wakaba 1.163 } elsif ($self->{state} == ENTITY_STATE) {
4380 wakaba 1.164 if ({
4381     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
4382     0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, &
4383 wakaba 1.179 $self->{entity_add} => 1,
4384     }->{$self->{nc}}) {
4385 wakaba 1.164
4386     ## Don't consume
4387     ## No error
4388     ## Return nothing.
4389     #
4390 wakaba 1.179 } elsif ($self->{nc} == 0x0023) { # #
4391 wakaba 1.166
4392 wakaba 1.164 $self->{state} = ENTITY_HASH_STATE;
4393 wakaba 1.179 $self->{s_kwd} = '#';
4394 wakaba 1.164
4395 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4396     $self->{line_prev} = $self->{line};
4397     $self->{column_prev} = $self->{column};
4398     $self->{column}++;
4399 wakaba 1.179 $self->{nc}
4400 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4401     } else {
4402 wakaba 1.179 $self->{set_nc}->($self);
4403 wakaba 1.175 }
4404 wakaba 1.1
4405 wakaba 1.164 redo A;
4406 wakaba 1.179 } elsif ((0x0041 <= $self->{nc} and
4407     $self->{nc} <= 0x005A) or # A..Z
4408     (0x0061 <= $self->{nc} and
4409     $self->{nc} <= 0x007A)) { # a..z
4410 wakaba 1.166
4411 wakaba 1.164 require Whatpm::_NamedEntityList;
4412     $self->{state} = ENTITY_NAME_STATE;
4413 wakaba 1.179 $self->{s_kwd} = chr $self->{nc};
4414     $self->{entity__value} = $self->{s_kwd};
4415 wakaba 1.164 $self->{entity__match} = 0;
4416 wakaba 1.1
4417 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4418     $self->{line_prev} = $self->{line};
4419     $self->{column_prev} = $self->{column};
4420     $self->{column}++;
4421 wakaba 1.179 $self->{nc}
4422 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4423     } else {
4424 wakaba 1.179 $self->{set_nc}->($self);
4425 wakaba 1.175 }
4426 wakaba 1.1
4427 wakaba 1.164 redo A;
4428     } else {
4429    
4430     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare ero');
4431     ## Return nothing.
4432     #
4433     }
4434    
4435     ## NOTE: No character is consumed by the "consume a character
4436     ## reference" algorithm. In other word, there is an "&" character
4437     ## that does not introduce a character reference, which would be
4438     ## appended to the parent element or the attribute value in later
4439     ## process of the tokenizer.
4440    
4441 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4442 wakaba 1.166
4443 wakaba 1.165 $self->{state} = $self->{prev_state};
4444 wakaba 1.164 ## Reconsume.
4445     return ({type => CHARACTER_TOKEN, data => '&',
4446     line => $self->{line_prev},
4447     column => $self->{column_prev},
4448     });
4449     redo A;
4450 wakaba 1.165 } else {
4451 wakaba 1.166
4452 wakaba 1.179 $self->{ca}->{value} .= '&';
4453 wakaba 1.165 $self->{state} = $self->{prev_state};
4454     ## Reconsume.
4455     redo A;
4456 wakaba 1.164 }
4457     } elsif ($self->{state} == ENTITY_HASH_STATE) {
4458 wakaba 1.179 if ($self->{nc} == 0x0078 or # x
4459     $self->{nc} == 0x0058) { # X
4460 wakaba 1.166
4461 wakaba 1.164 $self->{state} = HEXREF_X_STATE;
4462 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
4463 wakaba 1.164
4464 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4465     $self->{line_prev} = $self->{line};
4466     $self->{column_prev} = $self->{column};
4467     $self->{column}++;
4468 wakaba 1.179 $self->{nc}
4469 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4470     } else {
4471 wakaba 1.179 $self->{set_nc}->($self);
4472 wakaba 1.175 }
4473 wakaba 1.1
4474 wakaba 1.163 redo A;
4475 wakaba 1.179 } elsif (0x0030 <= $self->{nc} and
4476     $self->{nc} <= 0x0039) { # 0..9
4477 wakaba 1.166
4478 wakaba 1.164 $self->{state} = NCR_NUM_STATE;
4479 wakaba 1.179 $self->{s_kwd} = $self->{nc} - 0x0030;
4480 wakaba 1.164
4481 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4482     $self->{line_prev} = $self->{line};
4483     $self->{column_prev} = $self->{column};
4484     $self->{column}++;
4485 wakaba 1.179 $self->{nc}
4486 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4487     } else {
4488 wakaba 1.179 $self->{set_nc}->($self);
4489 wakaba 1.175 }
4490 wakaba 1.1
4491 wakaba 1.164 redo A;
4492     } else {
4493     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare nero',
4494     line => $self->{line_prev},
4495     column => $self->{column_prev} - 1);
4496    
4497     ## NOTE: According to the spec algorithm, nothing is returned,
4498     ## and then "&#" is appended to the parent element or the attribute
4499     ## value in the later processing.
4500    
4501 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4502 wakaba 1.166
4503 wakaba 1.165 $self->{state} = $self->{prev_state};
4504 wakaba 1.164 ## Reconsume.
4505     return ({type => CHARACTER_TOKEN,
4506     data => '&#',
4507     line => $self->{line_prev},
4508     column => $self->{column_prev} - 1,
4509     });
4510     redo A;
4511 wakaba 1.165 } else {
4512 wakaba 1.166
4513 wakaba 1.179 $self->{ca}->{value} .= '&#';
4514 wakaba 1.165 $self->{state} = $self->{prev_state};
4515     ## Reconsume.
4516     redo A;
4517 wakaba 1.164 }
4518     }
4519     } elsif ($self->{state} == NCR_NUM_STATE) {
4520 wakaba 1.179 if (0x0030 <= $self->{nc} and
4521     $self->{nc} <= 0x0039) { # 0..9
4522 wakaba 1.78
4523 wakaba 1.179 $self->{s_kwd} *= 10;
4524     $self->{s_kwd} += $self->{nc} - 0x0030;
4525 wakaba 1.1
4526 wakaba 1.164 ## Stay in the state.
4527 wakaba 1.1
4528 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4529     $self->{line_prev} = $self->{line};
4530     $self->{column_prev} = $self->{column};
4531     $self->{column}++;
4532 wakaba 1.179 $self->{nc}
4533 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4534     } else {
4535 wakaba 1.179 $self->{set_nc}->($self);
4536 wakaba 1.175 }
4537 wakaba 1.1
4538 wakaba 1.164 redo A;
4539 wakaba 1.179 } elsif ($self->{nc} == 0x003B) { # ;
4540 wakaba 1.1
4541 wakaba 1.78
4542 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4543     $self->{line_prev} = $self->{line};
4544     $self->{column_prev} = $self->{column};
4545     $self->{column}++;
4546 wakaba 1.179 $self->{nc}
4547 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4548     } else {
4549 wakaba 1.179 $self->{set_nc}->($self);
4550 wakaba 1.175 }
4551 wakaba 1.1
4552 wakaba 1.164 #
4553 wakaba 1.1 } else {
4554 wakaba 1.78
4555 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc');
4556     ## Reconsume.
4557     #
4558 wakaba 1.1 }
4559    
4560 wakaba 1.179 my $code = $self->{s_kwd};
4561 wakaba 1.164 my $l = $self->{line_prev};
4562     my $c = $self->{column_prev};
4563 wakaba 1.26 if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
4564 wakaba 1.78
4565 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4566     text => (sprintf 'U+%04X', $code),
4567     line => $l, column => $c);
4568 wakaba 1.26 $code = 0xFFFD;
4569     } elsif ($code > 0x10FFFF) {
4570 wakaba 1.78
4571 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4572     text => (sprintf 'U-%08X', $code),
4573     line => $l, column => $c);
4574 wakaba 1.26 $code = 0xFFFD;
4575     } elsif ($code == 0x000D) {
4576 wakaba 1.78
4577 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'CR character reference',
4578     line => $l, column => $c);
4579 wakaba 1.26 $code = 0x000A;
4580 wakaba 1.4 } elsif (0x80 <= $code and $code <= 0x9F) {
4581 wakaba 1.78
4582 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'C1 character reference',
4583     text => (sprintf 'U+%04X', $code),
4584     line => $l, column => $c);
4585 wakaba 1.4 $code = $c1_entity_char->{$code};
4586 wakaba 1.1 }
4587 wakaba 1.164
4588 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4589 wakaba 1.166
4590 wakaba 1.165 $self->{state} = $self->{prev_state};
4591 wakaba 1.164 ## Reconsume.
4592 wakaba 1.165 return ({type => CHARACTER_TOKEN, data => chr $code,
4593     line => $l, column => $c,
4594     });
4595 wakaba 1.164 redo A;
4596     } else {
4597 wakaba 1.166
4598 wakaba 1.179 $self->{ca}->{value} .= chr $code;
4599     $self->{ca}->{has_reference} = 1;
4600 wakaba 1.165 $self->{state} = $self->{prev_state};
4601 wakaba 1.164 ## Reconsume.
4602     redo A;
4603     }
4604     } elsif ($self->{state} == HEXREF_X_STATE) {
4605 wakaba 1.179 if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
4606     (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
4607     (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
4608 wakaba 1.164 # 0..9, A..F, a..f
4609 wakaba 1.166
4610 wakaba 1.164 $self->{state} = HEXREF_HEX_STATE;
4611 wakaba 1.179 $self->{s_kwd} = 0;
4612 wakaba 1.164 ## Reconsume.
4613     redo A;
4614     } else {
4615     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare hcro',
4616     line => $self->{line_prev},
4617     column => $self->{column_prev} - 2);
4618    
4619     ## NOTE: According to the spec algorithm, nothing is returned,
4620     ## and then "&#" followed by "X" or "x" is appended to the parent
4621     ## element or the attribute value in the later processing.
4622    
4623 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4624 wakaba 1.166
4625 wakaba 1.165 $self->{state} = $self->{prev_state};
4626 wakaba 1.164 ## Reconsume.
4627     return ({type => CHARACTER_TOKEN,
4628 wakaba 1.179 data => '&' . $self->{s_kwd},
4629 wakaba 1.164 line => $self->{line_prev},
4630 wakaba 1.179 column => $self->{column_prev} - length $self->{s_kwd},
4631 wakaba 1.164 });
4632     redo A;
4633 wakaba 1.165 } else {
4634 wakaba 1.166
4635 wakaba 1.179 $self->{ca}->{value} .= '&' . $self->{s_kwd};
4636 wakaba 1.165 $self->{state} = $self->{prev_state};
4637     ## Reconsume.
4638     redo A;
4639 wakaba 1.164 }
4640     }
4641     } elsif ($self->{state} == HEXREF_HEX_STATE) {
4642 wakaba 1.179 if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
4643 wakaba 1.164 # 0..9
4644    
4645 wakaba 1.179 $self->{s_kwd} *= 0x10;
4646     $self->{s_kwd} += $self->{nc} - 0x0030;
4647 wakaba 1.164 ## Stay in the state.
4648    
4649 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4650     $self->{line_prev} = $self->{line};
4651     $self->{column_prev} = $self->{column};
4652     $self->{column}++;
4653 wakaba 1.179 $self->{nc}
4654 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4655     } else {
4656 wakaba 1.179 $self->{set_nc}->($self);
4657 wakaba 1.175 }
4658 wakaba 1.164
4659     redo A;
4660 wakaba 1.179 } elsif (0x0061 <= $self->{nc} and
4661     $self->{nc} <= 0x0066) { # a..f
4662 wakaba 1.164
4663 wakaba 1.179 $self->{s_kwd} *= 0x10;
4664     $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
4665 wakaba 1.164 ## Stay in the state.
4666    
4667 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4668     $self->{line_prev} = $self->{line};
4669     $self->{column_prev} = $self->{column};
4670     $self->{column}++;
4671 wakaba 1.179 $self->{nc}
4672 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4673     } else {
4674 wakaba 1.179 $self->{set_nc}->($self);
4675 wakaba 1.175 }
4676 wakaba 1.1
4677 wakaba 1.164 redo A;
4678 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
4679     $self->{nc} <= 0x0046) { # A..F
4680 wakaba 1.164
4681 wakaba 1.179 $self->{s_kwd} *= 0x10;
4682     $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
4683 wakaba 1.164 ## Stay in the state.
4684    
4685 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4686     $self->{line_prev} = $self->{line};
4687     $self->{column_prev} = $self->{column};
4688     $self->{column}++;
4689 wakaba 1.179 $self->{nc}
4690 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4691     } else {
4692 wakaba 1.179 $self->{set_nc}->($self);
4693 wakaba 1.175 }
4694 wakaba 1.16
4695 wakaba 1.164 redo A;
4696 wakaba 1.179 } elsif ($self->{nc} == 0x003B) { # ;
4697 wakaba 1.164
4698    
4699 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4700     $self->{line_prev} = $self->{line};
4701     $self->{column_prev} = $self->{column};
4702     $self->{column}++;
4703 wakaba 1.179 $self->{nc}
4704 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4705     } else {
4706 wakaba 1.179 $self->{set_nc}->($self);
4707 wakaba 1.175 }
4708 wakaba 1.37
4709 wakaba 1.164 #
4710 wakaba 1.1 } else {
4711 wakaba 1.78
4712 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc',
4713     line => $self->{line},
4714     column => $self->{column});
4715     ## Reconsume.
4716     #
4717     }
4718    
4719 wakaba 1.179 my $code = $self->{s_kwd};
4720 wakaba 1.164 my $l = $self->{line_prev};
4721     my $c = $self->{column_prev};
4722     if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
4723    
4724     $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4725     text => (sprintf 'U+%04X', $code),
4726     line => $l, column => $c);
4727     $code = 0xFFFD;
4728     } elsif ($code > 0x10FFFF) {
4729 wakaba 1.37
4730 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4731     text => (sprintf 'U-%08X', $code),
4732     line => $l, column => $c);
4733     $code = 0xFFFD;
4734     } elsif ($code == 0x000D) {
4735    
4736     $self->{parse_error}->(level => $self->{level}->{must}, type => 'CR character reference', line => $l, column => $c);
4737     $code = 0x000A;
4738     } elsif (0x80 <= $code and $code <= 0x9F) {
4739    
4740     $self->{parse_error}->(level => $self->{level}->{must}, type => 'C1 character reference', text => (sprintf 'U+%04X', $code), line => $l, column => $c);
4741     $code = $c1_entity_char->{$code};
4742     }
4743    
4744 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4745 wakaba 1.166
4746 wakaba 1.165 $self->{state} = $self->{prev_state};
4747 wakaba 1.164 ## Reconsume.
4748 wakaba 1.165 return ({type => CHARACTER_TOKEN, data => chr $code,
4749     line => $l, column => $c,
4750     });
4751 wakaba 1.164 redo A;
4752     } else {
4753 wakaba 1.166
4754 wakaba 1.179 $self->{ca}->{value} .= chr $code;
4755     $self->{ca}->{has_reference} = 1;
4756 wakaba 1.165 $self->{state} = $self->{prev_state};
4757 wakaba 1.164 ## Reconsume.
4758     redo A;
4759     }
4760     } elsif ($self->{state} == ENTITY_NAME_STATE) {
4761 wakaba 1.179 if (length $self->{s_kwd} < 30 and
4762 wakaba 1.164 ## NOTE: Some number greater than the maximum length of entity name
4763 wakaba 1.179 ((0x0041 <= $self->{nc} and # a
4764     $self->{nc} <= 0x005A) or # x
4765     (0x0061 <= $self->{nc} and # a
4766     $self->{nc} <= 0x007A) or # z
4767     (0x0030 <= $self->{nc} and # 0
4768     $self->{nc} <= 0x0039) or # 9
4769     $self->{nc} == 0x003B)) { # ;
4770 wakaba 1.164 our $EntityChar;
4771 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
4772     if (defined $EntityChar->{$self->{s_kwd}}) {
4773     if ($self->{nc} == 0x003B) { # ;
4774 wakaba 1.164
4775 wakaba 1.179 $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
4776 wakaba 1.164 $self->{entity__match} = 1;
4777    
4778 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4779     $self->{line_prev} = $self->{line};
4780     $self->{column_prev} = $self->{column};
4781     $self->{column}++;
4782 wakaba 1.179 $self->{nc}
4783 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4784     } else {
4785 wakaba 1.179 $self->{set_nc}->($self);
4786 wakaba 1.175 }
4787 wakaba 1.1
4788 wakaba 1.164 #
4789     } else {
4790    
4791 wakaba 1.179 $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
4792 wakaba 1.164 $self->{entity__match} = -1;
4793     ## Stay in the state.
4794    
4795 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4796     $self->{line_prev} = $self->{line};
4797     $self->{column_prev} = $self->{column};
4798     $self->{column}++;
4799 wakaba 1.179 $self->{nc}
4800 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4801     } else {
4802 wakaba 1.179 $self->{set_nc}->($self);
4803 wakaba 1.175 }
4804 wakaba 1.164
4805     redo A;
4806     }
4807     } else {
4808    
4809 wakaba 1.179 $self->{entity__value} .= chr $self->{nc};
4810 wakaba 1.164 $self->{entity__match} *= 2;
4811     ## Stay in the state.
4812    
4813 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4814     $self->{line_prev} = $self->{line};
4815     $self->{column_prev} = $self->{column};
4816     $self->{column}++;
4817 wakaba 1.179 $self->{nc}
4818 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4819     } else {
4820 wakaba 1.179 $self->{set_nc}->($self);
4821 wakaba 1.175 }
4822 wakaba 1.164
4823     redo A;
4824     }
4825     }
4826    
4827     my $data;
4828     my $has_ref;
4829     if ($self->{entity__match} > 0) {
4830    
4831     $data = $self->{entity__value};
4832     $has_ref = 1;
4833     #
4834     } elsif ($self->{entity__match} < 0) {
4835     $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc');
4836 wakaba 1.165 if ($self->{prev_state} != DATA_STATE and # in attribute
4837     $self->{entity__match} < -1) {
4838 wakaba 1.164
4839 wakaba 1.179 $data = '&' . $self->{s_kwd};
4840 wakaba 1.164 #
4841     } else {
4842    
4843     $data = $self->{entity__value};
4844     $has_ref = 1;
4845     #
4846     }
4847     } else {
4848 wakaba 1.78
4849 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare ero',
4850     line => $self->{line_prev},
4851 wakaba 1.179 column => $self->{column_prev} - length $self->{s_kwd});
4852     $data = '&' . $self->{s_kwd};
4853 wakaba 1.164 #
4854     }
4855    
4856     ## NOTE: In these cases, when a character reference is found,
4857     ## it is consumed and a character token is returned, or, otherwise,
4858     ## nothing is consumed and returned, according to the spec algorithm.
4859     ## In this implementation, anything that has been examined by the
4860     ## tokenizer is appended to the parent element or the attribute value
4861     ## as string, either literal string when no character reference or
4862     ## entity-replaced string otherwise, in this stage, since any characters
4863     ## that would not be consumed are appended in the data state or in an
4864     ## appropriate attribute value state anyway.
4865    
4866 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4867 wakaba 1.166
4868 wakaba 1.165 $self->{state} = $self->{prev_state};
4869 wakaba 1.164 ## Reconsume.
4870     return ({type => CHARACTER_TOKEN,
4871 wakaba 1.165 data => $data,
4872 wakaba 1.164 line => $self->{line_prev},
4873 wakaba 1.179 column => $self->{column_prev} + 1 - length $self->{s_kwd},
4874 wakaba 1.164 });
4875 wakaba 1.163 redo A;
4876 wakaba 1.165 } else {
4877 wakaba 1.166
4878 wakaba 1.179 $self->{ca}->{value} .= $data;
4879     $self->{ca}->{has_reference} = 1 if $has_ref;
4880 wakaba 1.165 $self->{state} = $self->{prev_state};
4881     ## Reconsume.
4882     redo A;
4883 wakaba 1.37 }
4884 wakaba 1.1 } else {
4885 wakaba 1.163 die "$0: $self->{state}: Unknown state";
4886     }
4887     } # A
4888    
4889     die "$0: _get_next_token: unexpected case";
4890     } # _get_next_token
4891 wakaba 1.1
4892     sub _initialize_tree_constructor ($) {
4893     my $self = shift;
4894     ## NOTE: $self->{document} MUST be specified before this method is called
4895     $self->{document}->strict_error_checking (0);
4896     ## TODO: Turn mutation events off # MUST
4897     ## TODO: Turn loose Document option (manakai extension) on
4898 wakaba 1.18 $self->{document}->manakai_is_html (1); # MUST
4899 wakaba 1.151 $self->{document}->set_user_data (manakai_source_line => 1);
4900     $self->{document}->set_user_data (manakai_source_column => 1);
4901 wakaba 1.1 } # _initialize_tree_constructor
4902    
4903     sub _terminate_tree_constructor ($) {
4904     my $self = shift;
4905     $self->{document}->strict_error_checking (1);
4906     ## TODO: Turn mutation events on
4907     } # _terminate_tree_constructor
4908    
4909     ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?
4910    
4911 wakaba 1.3 { # tree construction stage
4912     my $token;
4913    
4914 wakaba 1.1 sub _construct_tree ($) {
4915     my ($self) = @_;
4916    
4917     ## When an interactive UA render the $self->{document} available
4918     ## to the user, or when it begin accepting user input, are
4919     ## not defined.
4920    
4921     ## Append a character: collect it and all subsequent consecutive
4922     ## characters and insert one Text node whose data is concatenation
4923     ## of all those characters. # MUST
4924    
4925     $token = $self->_get_next_token;
4926    
4927 wakaba 1.3 undef $self->{form_element};
4928     undef $self->{head_element};
4929     $self->{open_elements} = [];
4930     undef $self->{inner_html_node};
4931    
4932 wakaba 1.84 ## NOTE: The "initial" insertion mode.
4933 wakaba 1.3 $self->_tree_construction_initial; # MUST
4934 wakaba 1.84
4935 wakaba 1.85 ## NOTE: The "before html" insertion mode.
4936 wakaba 1.3 $self->_tree_construction_root_element;
4937 wakaba 1.84 $self->{insertion_mode} = BEFORE_HEAD_IM;
4938    
4939     ## NOTE: The "before head" insertion mode and so on.
4940 wakaba 1.3 $self->_tree_construction_main;
4941     } # _construct_tree
4942    
4943     sub _tree_construction_initial ($) {
4944     my $self = shift;
4945 wakaba 1.84
4946     ## NOTE: "initial" insertion mode
4947    
4948 wakaba 1.18 INITIAL: {
4949 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
4950 wakaba 1.18 ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
4951     ## error, switch to a conformance checking mode for another
4952     ## language.
4953     my $doctype_name = $token->{name};
4954     $doctype_name = '' unless defined $doctype_name;
4955 wakaba 1.155 $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
4956 wakaba 1.18 if (not defined $token->{name} or # <!DOCTYPE>
4957 wakaba 1.179 defined $token->{sysid}) {
4958 wakaba 1.79
4959 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
4960 wakaba 1.18 } elsif ($doctype_name ne 'HTML') {
4961 wakaba 1.79
4962 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
4963 wakaba 1.179 } elsif (defined $token->{pubid}) {
4964     if ($token->{pubid} eq 'XSLT-compat') {
4965 wakaba 1.155
4966     $self->{parse_error}->(level => $self->{level}->{must}, type => 'XSLT-compat', token => $token,
4967     level => $self->{level}->{should});
4968     } else {
4969     $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
4970     }
4971 wakaba 1.79 } else {
4972    
4973 wakaba 1.155 #
4974 wakaba 1.18 }
4975    
4976     my $doctype = $self->{document}->create_document_type_definition
4977     ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
4978 wakaba 1.120 ## NOTE: Default value for both |public_id| and |system_id| attributes
4979     ## are empty strings, so that we don't set any value in missing cases.
4980 wakaba 1.179 $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
4981     $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
4982 wakaba 1.18 ## NOTE: Other DocumentType attributes are null or empty lists.
4983     ## ISSUE: internalSubset = null??
4984     $self->{document}->append_child ($doctype);
4985    
4986 wakaba 1.75 if ($token->{quirks} or $doctype_name ne 'HTML') {
4987 wakaba 1.79
4988 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
4989 wakaba 1.179 } elsif (defined $token->{pubid}) {
4990     my $pubid = $token->{pubid};
4991 wakaba 1.18 $pubid =~ tr/a-z/A-z/;
4992 wakaba 1.140 my $prefix = [
4993     "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
4994     "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
4995     "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
4996     "-//IETF//DTD HTML 2.0 LEVEL 1//",
4997     "-//IETF//DTD HTML 2.0 LEVEL 2//",
4998     "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
4999     "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
5000     "-//IETF//DTD HTML 2.0 STRICT//",
5001     "-//IETF//DTD HTML 2.0//",
5002     "-//IETF//DTD HTML 2.1E//",
5003     "-//IETF//DTD HTML 3.0//",
5004     "-//IETF//DTD HTML 3.2 FINAL//",
5005     "-//IETF//DTD HTML 3.2//",
5006     "-//IETF//DTD HTML 3//",
5007     "-//IETF//DTD HTML LEVEL 0//",
5008     "-//IETF//DTD HTML LEVEL 1//",
5009     "-//IETF//DTD HTML LEVEL 2//",
5010     "-//IETF//DTD HTML LEVEL 3//",
5011     "-//IETF//DTD HTML STRICT LEVEL 0//",
5012     "-//IETF//DTD HTML STRICT LEVEL 1//",
5013     "-//IETF//DTD HTML STRICT LEVEL 2//",
5014     "-//IETF//DTD HTML STRICT LEVEL 3//",
5015     "-//IETF//DTD HTML STRICT//",
5016     "-//IETF//DTD HTML//",
5017     "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
5018     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
5019     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
5020     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
5021     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
5022     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
5023     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
5024     "-//NETSCAPE COMM. CORP.//DTD HTML//",
5025     "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
5026     "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
5027     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
5028     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
5029     "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
5030     "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
5031     "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
5032     "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
5033     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
5034     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
5035     "-//W3C//DTD HTML 3 1995-03-24//",
5036     "-//W3C//DTD HTML 3.2 DRAFT//",
5037     "-//W3C//DTD HTML 3.2 FINAL//",
5038     "-//W3C//DTD HTML 3.2//",
5039     "-//W3C//DTD HTML 3.2S DRAFT//",
5040     "-//W3C//DTD HTML 4.0 FRAMESET//",
5041     "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
5042     "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
5043     "-//W3C//DTD HTML EXPERIMENTAL 970421//",
5044     "-//W3C//DTD W3 HTML//",
5045     "-//W3O//DTD W3 HTML 3.0//",
5046     "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
5047     "-//WEBTECHS//DTD MOZILLA HTML//",
5048     ]; # $prefix
5049     my $match;
5050     for (@$prefix) {
5051     if (substr ($prefix, 0, length $_) eq $_) {
5052     $match = 1;
5053     last;
5054     }
5055     }
5056     if ($match or
5057     $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
5058     $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
5059     $pubid eq "HTML") {
5060 wakaba 1.79
5061 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5062 wakaba 1.140 } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
5063     $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
5064 wakaba 1.179 if (defined $token->{sysid}) {
5065 wakaba 1.79
5066 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5067     } else {
5068 wakaba 1.79
5069 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
5070 wakaba 1.3 }
5071 wakaba 1.140 } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
5072     $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
5073 wakaba 1.79
5074 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
5075 wakaba 1.79 } else {
5076    
5077 wakaba 1.18 }
5078 wakaba 1.79 } else {
5079    
5080 wakaba 1.18 }
5081 wakaba 1.179 if (defined $token->{sysid}) {
5082     my $sysid = $token->{sysid};
5083 wakaba 1.18 $sysid =~ tr/A-Z/a-z/;
5084     if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
5085 wakaba 1.140 ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
5086     ## marked as quirks.
5087 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5088 wakaba 1.79
5089     } else {
5090    
5091 wakaba 1.18 }
5092 wakaba 1.79 } else {
5093    
5094 wakaba 1.18 }
5095    
5096 wakaba 1.85 ## Go to the "before html" insertion mode.
5097 wakaba 1.18 $token = $self->_get_next_token;
5098     return;
5099     } elsif ({
5100 wakaba 1.57 START_TAG_TOKEN, 1,
5101     END_TAG_TOKEN, 1,
5102     END_OF_FILE_TOKEN, 1,
5103 wakaba 1.18 }->{$token->{type}}) {
5104 wakaba 1.79
5105 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE', token => $token);
5106 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5107 wakaba 1.85 ## Go to the "before html" insertion mode.
5108 wakaba 1.18 ## reprocess
5109 wakaba 1.122
5110 wakaba 1.18 return;
5111 wakaba 1.57 } elsif ($token->{type} == CHARACTER_TOKEN) {
5112 wakaba 1.18 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
5113     ## Ignore the token
5114 wakaba 1.26
5115 wakaba 1.18 unless (length $token->{data}) {
5116 wakaba 1.79
5117 wakaba 1.84 ## Stay in the insertion mode.
5118 wakaba 1.18 $token = $self->_get_next_token;
5119     redo INITIAL;
5120 wakaba 1.79 } else {
5121    
5122 wakaba 1.3 }
5123 wakaba 1.79 } else {
5124    
5125 wakaba 1.3 }
5126 wakaba 1.18
5127 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE', token => $token);
5128 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5129 wakaba 1.85 ## Go to the "before html" insertion mode.
5130 wakaba 1.18 ## reprocess
5131     return;
5132 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
5133 wakaba 1.79
5134 wakaba 1.18 my $comment = $self->{document}->create_comment ($token->{data});
5135     $self->{document}->append_child ($comment);
5136    
5137 wakaba 1.84 ## Stay in the insertion mode.
5138 wakaba 1.18 $token = $self->_get_next_token;
5139     redo INITIAL;
5140     } else {
5141 wakaba 1.57 die "$0: $token->{type}: Unknown token type";
5142 wakaba 1.18 }
5143     } # INITIAL
5144 wakaba 1.79
5145     die "$0: _tree_construction_initial: This should be never reached";
5146 wakaba 1.3 } # _tree_construction_initial
5147    
5148     sub _tree_construction_root_element ($) {
5149     my $self = shift;
5150 wakaba 1.84
5151 wakaba 1.85 ## NOTE: "before html" insertion mode.
5152 wakaba 1.3
5153     B: {
5154 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
5155 wakaba 1.79
5156 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in html:#DOCTYPE', token => $token);
5157 wakaba 1.3 ## Ignore the token
5158 wakaba 1.84 ## Stay in the insertion mode.
5159 wakaba 1.3 $token = $self->_get_next_token;
5160     redo B;
5161 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
5162 wakaba 1.79
5163 wakaba 1.3 my $comment = $self->{document}->create_comment ($token->{data});
5164     $self->{document}->append_child ($comment);
5165 wakaba 1.84 ## Stay in the insertion mode.
5166 wakaba 1.3 $token = $self->_get_next_token;
5167     redo B;
5168 wakaba 1.57 } elsif ($token->{type} == CHARACTER_TOKEN) {
5169 wakaba 1.26 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
5170     ## Ignore the token.
5171    
5172 wakaba 1.3 unless (length $token->{data}) {
5173 wakaba 1.79
5174 wakaba 1.84 ## Stay in the insertion mode.
5175 wakaba 1.3 $token = $self->_get_next_token;
5176     redo B;
5177 wakaba 1.79 } else {
5178    
5179 wakaba 1.3 }
5180 wakaba 1.79 } else {
5181    
5182 wakaba 1.3 }
5183 wakaba 1.63
5184     $self->{application_cache_selection}->(undef);
5185    
5186     #
5187     } elsif ($token->{type} == START_TAG_TOKEN) {
5188 wakaba 1.84 if ($token->{tag_name} eq 'html') {
5189     my $root_element;
5190 wakaba 1.79
5191 wakaba 1.84 $root_element = $self->{document}->create_element_ns
5192 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5193 wakaba 1.84
5194     for my $attr_name (keys %{ $token->{attributes}}) {
5195 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5196 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5197 wakaba 1.117 $attr->value ($attr_t->{value});
5198     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5199     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5200     $root_element->set_attribute_node_ns ($attr);
5201 wakaba 1.84 }
5202    
5203 wakaba 1.114 $root_element->set_user_data (manakai_source_line => $token->{line})
5204     if defined $token->{line};
5205     $root_element->set_user_data (manakai_source_column => $token->{column})
5206     if defined $token->{column};
5207    
5208 wakaba 1.84 $self->{document}->append_child ($root_element);
5209 wakaba 1.121 push @{$self->{open_elements}},
5210     [$root_element, $el_category->{html}];
5211 wakaba 1.84
5212     if ($token->{attributes}->{manifest}) {
5213    
5214     $self->{application_cache_selection}
5215     ->($token->{attributes}->{manifest}->{value});
5216 wakaba 1.116 ## ISSUE: Spec is unclear on relative references.
5217     ## According to Hixie (#whatwg 2008-03-19), it should be
5218     ## resolved against the base URI of the document in HTML
5219     ## or xml:base of the element in XHTML.
5220 wakaba 1.84 } else {
5221    
5222     $self->{application_cache_selection}->(undef);
5223     }
5224    
5225 wakaba 1.122
5226    
5227 wakaba 1.84 $token = $self->_get_next_token;
5228     return; ## Go to the "before head" insertion mode.
5229 wakaba 1.63 } else {
5230 wakaba 1.79
5231 wakaba 1.84 #
5232 wakaba 1.63 }
5233 wakaba 1.3 } elsif ({
5234 wakaba 1.57 END_TAG_TOKEN, 1,
5235     END_OF_FILE_TOKEN, 1,
5236 wakaba 1.3 }->{$token->{type}}) {
5237 wakaba 1.79
5238 wakaba 1.3 #
5239     } else {
5240 wakaba 1.57 die "$0: $token->{type}: Unknown token type";
5241 wakaba 1.3 }
5242 wakaba 1.63
5243 wakaba 1.123 my $root_element;
5244    
5245 wakaba 1.3 $root_element = $self->{document}->create_element_ns
5246 wakaba 1.128 ($HTML_NS, [undef, 'html']);
5247 wakaba 1.3
5248 wakaba 1.114 $root_element->set_user_data (manakai_source_line => $token->{line})
5249     if defined $token->{line};
5250     $root_element->set_user_data (manakai_source_column => $token->{column})
5251     if defined $token->{column};
5252    
5253 wakaba 1.84 $self->{document}->append_child ($root_element);
5254 wakaba 1.121 push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
5255 wakaba 1.84
5256     $self->{application_cache_selection}->(undef);
5257    
5258     ## NOTE: Reprocess the token.
5259 wakaba 1.122
5260 wakaba 1.84 return; ## Go to the "before head" insertion mode.
5261    
5262     ## ISSUE: There is an issue in the spec
5263 wakaba 1.3 } # B
5264 wakaba 1.79
5265     die "$0: _tree_construction_root_element: This should never be reached";
5266 wakaba 1.3 } # _tree_construction_root_element
5267    
5268     sub _reset_insertion_mode ($) {
5269     my $self = shift;
5270    
5271     ## Step 1
5272     my $last;
5273    
5274     ## Step 2
5275     my $i = -1;
5276     my $node = $self->{open_elements}->[$i];
5277    
5278     ## Step 3
5279     S3: {
5280 wakaba 1.29 if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
5281     $last = 1;
5282     if (defined $self->{inner_html_node}) {
5283 wakaba 1.137
5284     $node = $self->{inner_html_node};
5285     } else {
5286     die "_reset_insertion_mode: t27";
5287 wakaba 1.3 }
5288     }
5289 wakaba 1.137
5290     ## Step 4..14
5291     my $new_mode;
5292     if ($node->[1] & FOREIGN_EL) {
5293    
5294     ## NOTE: Strictly spaking, the line below only applies to MathML and
5295     ## SVG elements. Currently the HTML syntax supports only MathML and
5296     ## SVG elements as foreigners.
5297 wakaba 1.145 $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
5298 wakaba 1.137 } elsif ($node->[1] & TABLE_CELL_EL) {
5299     if ($last) {
5300    
5301     #
5302     } else {
5303    
5304     $new_mode = IN_CELL_IM;
5305     }
5306     } else {
5307    
5308     $new_mode = {
5309 wakaba 1.56 select => IN_SELECT_IM,
5310 wakaba 1.83 ## NOTE: |option| and |optgroup| do not set
5311     ## insertion mode to "in select" by themselves.
5312 wakaba 1.56 tr => IN_ROW_IM,
5313     tbody => IN_TABLE_BODY_IM,
5314     thead => IN_TABLE_BODY_IM,
5315     tfoot => IN_TABLE_BODY_IM,
5316     caption => IN_CAPTION_IM,
5317     colgroup => IN_COLUMN_GROUP_IM,
5318     table => IN_TABLE_IM,
5319     head => IN_BODY_IM, # not in head!
5320     body => IN_BODY_IM,
5321     frameset => IN_FRAMESET_IM,
5322 wakaba 1.121 }->{$node->[0]->manakai_local_name};
5323 wakaba 1.137 }
5324     $self->{insertion_mode} = $new_mode and return if defined $new_mode;
5325 wakaba 1.3
5326 wakaba 1.123 ## Step 15
5327 wakaba 1.121 if ($node->[1] & HTML_EL) {
5328 wakaba 1.3 unless (defined $self->{head_element}) {
5329 wakaba 1.79
5330 wakaba 1.56 $self->{insertion_mode} = BEFORE_HEAD_IM;
5331 wakaba 1.3 } else {
5332 wakaba 1.81 ## ISSUE: Can this state be reached?
5333 wakaba 1.79
5334 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
5335 wakaba 1.3 }
5336     return;
5337 wakaba 1.79 } else {
5338    
5339 wakaba 1.3 }
5340    
5341 wakaba 1.123 ## Step 16
5342 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM and return if $last;
5343 wakaba 1.3
5344 wakaba 1.123 ## Step 17
5345 wakaba 1.3 $i--;
5346     $node = $self->{open_elements}->[$i];
5347    
5348 wakaba 1.123 ## Step 18
5349 wakaba 1.3 redo S3;
5350     } # S3
5351 wakaba 1.79
5352     die "$0: _reset_insertion_mode: This line should never be reached";
5353 wakaba 1.3 } # _reset_insertion_mode
5354    
5355     sub _tree_construction_main ($) {
5356     my $self = shift;
5357    
5358 wakaba 1.1 my $active_formatting_elements = [];
5359    
5360     my $reconstruct_active_formatting_elements = sub { # MUST
5361     my $insert = shift;
5362    
5363     ## Step 1
5364     return unless @$active_formatting_elements;
5365    
5366     ## Step 3
5367     my $i = -1;
5368     my $entry = $active_formatting_elements->[$i];
5369    
5370     ## Step 2
5371     return if $entry->[0] eq '#marker';
5372 wakaba 1.3 for (@{$self->{open_elements}}) {
5373 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
5374 wakaba 1.79
5375 wakaba 1.1 return;
5376     }
5377     }
5378    
5379     S4: {
5380     ## Step 4
5381     last S4 if $active_formatting_elements->[0]->[0] eq $entry->[0];
5382    
5383     ## Step 5
5384     $i--;
5385     $entry = $active_formatting_elements->[$i];
5386    
5387     ## Step 6
5388     if ($entry->[0] eq '#marker') {
5389 wakaba 1.79
5390 wakaba 1.1 #
5391     } else {
5392     my $in_open_elements;
5393 wakaba 1.3 OE: for (@{$self->{open_elements}}) {
5394 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
5395 wakaba 1.79
5396 wakaba 1.1 $in_open_elements = 1;
5397     last OE;
5398     }
5399     }
5400     if ($in_open_elements) {
5401 wakaba 1.79
5402 wakaba 1.1 #
5403     } else {
5404 wakaba 1.81 ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
5405 wakaba 1.79
5406 wakaba 1.1 redo S4;
5407     }
5408     }
5409    
5410     ## Step 7
5411     $i++;
5412     $entry = $active_formatting_elements->[$i];
5413     } # S4
5414    
5415     S7: {
5416     ## Step 8
5417     my $clone = [$entry->[0]->clone_node (0), $entry->[1]];
5418    
5419     ## Step 9
5420     $insert->($clone->[0]);
5421 wakaba 1.3 push @{$self->{open_elements}}, $clone;
5422 wakaba 1.1
5423     ## Step 10
5424 wakaba 1.3 $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
5425 wakaba 1.1
5426     ## Step 11
5427     unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
5428 wakaba 1.79
5429 wakaba 1.1 ## Step 7'
5430     $i++;
5431     $entry = $active_formatting_elements->[$i];
5432    
5433     redo S7;
5434     }
5435 wakaba 1.79
5436    
5437 wakaba 1.1 } # S7
5438     }; # $reconstruct_active_formatting_elements
5439    
5440     my $clear_up_to_marker = sub {
5441     for (reverse 0..$#$active_formatting_elements) {
5442     if ($active_formatting_elements->[$_]->[0] eq '#marker') {
5443 wakaba 1.79
5444 wakaba 1.1 splice @$active_formatting_elements, $_;
5445     return;
5446     }
5447     }
5448 wakaba 1.79
5449    
5450 wakaba 1.1 }; # $clear_up_to_marker
5451    
5452 wakaba 1.96 my $insert;
5453    
5454     my $parse_rcdata = sub ($) {
5455     my ($content_model_flag) = @_;
5456 wakaba 1.25
5457     ## Step 1
5458     my $start_tag_name = $token->{tag_name};
5459     my $el;
5460    
5461     $el = $self->{document}->create_element_ns
5462 wakaba 1.128 ($HTML_NS, [undef, $start_tag_name]);
5463 wakaba 1.1
5464 wakaba 1.6 for my $attr_name (keys %{ $token->{attributes}}) {
5465 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5466 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5467 wakaba 1.117 $attr->value ($attr_t->{value});
5468     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5469     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5470     $el->set_attribute_node_ns ($attr);
5471 wakaba 1.6 }
5472    
5473 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5474     if defined $token->{line};
5475     $el->set_user_data (manakai_source_column => $token->{column})
5476     if defined $token->{column};
5477    
5478 wakaba 1.25
5479     ## Step 2
5480 wakaba 1.96 $insert->($el);
5481 wakaba 1.25
5482     ## Step 3
5483 wakaba 1.41 $self->{content_model} = $content_model_flag; # CDATA or RCDATA
5484 wakaba 1.13 delete $self->{escape}; # MUST
5485 wakaba 1.25
5486     ## Step 4
5487 wakaba 1.1 my $text = '';
5488 wakaba 1.122
5489 wakaba 1.1 $token = $self->_get_next_token;
5490 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
5491 wakaba 1.79
5492 wakaba 1.1 $text .= $token->{data};
5493     $token = $self->_get_next_token;
5494 wakaba 1.25 }
5495    
5496     ## Step 5
5497 wakaba 1.1 if (length $text) {
5498 wakaba 1.79
5499 wakaba 1.25 my $text = $self->{document}->create_text_node ($text);
5500     $el->append_child ($text);
5501 wakaba 1.1 }
5502 wakaba 1.25
5503     ## Step 6
5504 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL;
5505 wakaba 1.25
5506     ## Step 7
5507 wakaba 1.79 if ($token->{type} == END_TAG_TOKEN and
5508     $token->{tag_name} eq $start_tag_name) {
5509    
5510 wakaba 1.1 ## Ignore the token
5511     } else {
5512 wakaba 1.96 ## NOTE: An end-of-file token.
5513     if ($content_model_flag == CDATA_CONTENT_MODEL) {
5514    
5515 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in CDATA:#eof', token => $token);
5516 wakaba 1.96 } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
5517    
5518 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in RCDATA:#eof', token => $token);
5519 wakaba 1.96 } else {
5520     die "$0: $content_model_flag in parse_rcdata";
5521     }
5522 wakaba 1.1 }
5523     $token = $self->_get_next_token;
5524 wakaba 1.25 }; # $parse_rcdata
5525 wakaba 1.1
5526 wakaba 1.96 my $script_start_tag = sub () {
5527 wakaba 1.1 my $script_el;
5528    
5529     $script_el = $self->{document}->create_element_ns
5530 wakaba 1.128 ($HTML_NS, [undef, 'script']);
5531 wakaba 1.1
5532     for my $attr_name (keys %{ $token->{attributes}}) {
5533 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5534 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5535 wakaba 1.117 $attr->value ($attr_t->{value});
5536     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5537     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5538     $script_el->set_attribute_node_ns ($attr);
5539 wakaba 1.1 }
5540    
5541 wakaba 1.114 $script_el->set_user_data (manakai_source_line => $token->{line})
5542     if defined $token->{line};
5543     $script_el->set_user_data (manakai_source_column => $token->{column})
5544     if defined $token->{column};
5545    
5546 wakaba 1.1 ## TODO: mark as "parser-inserted"
5547    
5548 wakaba 1.41 $self->{content_model} = CDATA_CONTENT_MODEL;
5549 wakaba 1.13 delete $self->{escape}; # MUST
5550 wakaba 1.1
5551     my $text = '';
5552 wakaba 1.122
5553 wakaba 1.1 $token = $self->_get_next_token;
5554 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) {
5555 wakaba 1.79
5556 wakaba 1.1 $text .= $token->{data};
5557     $token = $self->_get_next_token;
5558     } # stop if non-character token or tokenizer stops tokenising
5559     if (length $text) {
5560 wakaba 1.79
5561 wakaba 1.1 $script_el->manakai_append_text ($text);
5562     }
5563    
5564 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL;
5565 wakaba 1.1
5566 wakaba 1.57 if ($token->{type} == END_TAG_TOKEN and
5567 wakaba 1.1 $token->{tag_name} eq 'script') {
5568 wakaba 1.79
5569 wakaba 1.1 ## Ignore the token
5570     } else {
5571 wakaba 1.79
5572 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in CDATA:#eof', token => $token);
5573 wakaba 1.1 ## ISSUE: And ignore?
5574     ## TODO: mark as "already executed"
5575     }
5576    
5577 wakaba 1.3 if (defined $self->{inner_html_node}) {
5578 wakaba 1.79
5579 wakaba 1.3 ## TODO: mark as "already executed"
5580     } else {
5581 wakaba 1.79
5582 wakaba 1.1 ## TODO: $old_insertion_point = current insertion point
5583     ## TODO: insertion point = just before the next input character
5584 wakaba 1.25
5585     $insert->($script_el);
5586 wakaba 1.1
5587     ## TODO: insertion point = $old_insertion_point (might be "undefined")
5588    
5589     ## TODO: if there is a script that will execute as soon as the parser resume, then...
5590     }
5591    
5592     $token = $self->_get_next_token;
5593     }; # $script_start_tag
5594    
5595 wakaba 1.102 ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
5596     ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
5597     my $open_tables = [[$self->{open_elements}->[0]->[0]]];
5598    
5599 wakaba 1.1 my $formatting_end_tag = sub {
5600 wakaba 1.111 my $end_tag_token = shift;
5601     my $tag_name = $end_tag_token->{tag_name};
5602 wakaba 1.1
5603 wakaba 1.102 ## NOTE: The adoption agency algorithm (AAA).
5604    
5605 wakaba 1.1 FET: {
5606     ## Step 1
5607     my $formatting_element;
5608     my $formatting_element_i_in_active;
5609     AFE: for (reverse 0..$#$active_formatting_elements) {
5610 wakaba 1.121 if ($active_formatting_elements->[$_]->[0] eq '#marker') {
5611    
5612     last AFE;
5613     } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
5614     eq $tag_name) {
5615 wakaba 1.79
5616 wakaba 1.1 $formatting_element = $active_formatting_elements->[$_];
5617     $formatting_element_i_in_active = $_;
5618     last AFE;
5619     }
5620     } # AFE
5621     unless (defined $formatting_element) {
5622 wakaba 1.79
5623 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
5624 wakaba 1.1 ## Ignore the token
5625     $token = $self->_get_next_token;
5626     return;
5627     }
5628     ## has an element in scope
5629     my $in_scope = 1;
5630     my $formatting_element_i_in_open;
5631 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5632     my $node = $self->{open_elements}->[$_];
5633 wakaba 1.1 if ($node->[0] eq $formatting_element->[0]) {
5634     if ($in_scope) {
5635 wakaba 1.79
5636 wakaba 1.1 $formatting_element_i_in_open = $_;
5637     last INSCOPE;
5638     } else { # in open elements but not in scope
5639 wakaba 1.79
5640 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
5641     text => $token->{tag_name},
5642 wakaba 1.111 token => $end_tag_token);
5643 wakaba 1.1 ## Ignore the token
5644     $token = $self->_get_next_token;
5645     return;
5646     }
5647 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
5648 wakaba 1.79
5649 wakaba 1.1 $in_scope = 0;
5650     }
5651     } # INSCOPE
5652     unless (defined $formatting_element_i_in_open) {
5653 wakaba 1.79
5654 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
5655     text => $token->{tag_name},
5656 wakaba 1.111 token => $end_tag_token);
5657 wakaba 1.1 pop @$active_formatting_elements; # $formatting_element
5658     $token = $self->_get_next_token; ## TODO: ok?
5659     return;
5660     }
5661 wakaba 1.3 if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
5662 wakaba 1.79
5663 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
5664     text => $self->{open_elements}->[-1]->[0]
5665 wakaba 1.120 ->manakai_local_name,
5666 wakaba 1.111 token => $end_tag_token);
5667 wakaba 1.1 }
5668    
5669     ## Step 2
5670     my $furthest_block;
5671     my $furthest_block_i_in_open;
5672 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
5673     my $node = $self->{open_elements}->[$_];
5674 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
5675 wakaba 1.1 #not $phrasing_category->{$node->[1]} and
5676 wakaba 1.121 ($node->[1] & SPECIAL_EL or
5677     $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
5678 wakaba 1.79
5679 wakaba 1.1 $furthest_block = $node;
5680     $furthest_block_i_in_open = $_;
5681     } elsif ($node->[0] eq $formatting_element->[0]) {
5682 wakaba 1.79
5683 wakaba 1.1 last OE;
5684     }
5685     } # OE
5686    
5687     ## Step 3
5688     unless (defined $furthest_block) { # MUST
5689 wakaba 1.79
5690 wakaba 1.3 splice @{$self->{open_elements}}, $formatting_element_i_in_open;
5691 wakaba 1.1 splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
5692     $token = $self->_get_next_token;
5693     return;
5694     }
5695    
5696     ## Step 4
5697 wakaba 1.3 my $common_ancestor_node = $self->{open_elements}->[$formatting_element_i_in_open - 1];
5698 wakaba 1.1
5699     ## Step 5
5700     my $furthest_block_parent = $furthest_block->[0]->parent_node;
5701     if (defined $furthest_block_parent) {
5702 wakaba 1.79
5703 wakaba 1.1 $furthest_block_parent->remove_child ($furthest_block->[0]);
5704     }
5705    
5706     ## Step 6
5707     my $bookmark_prev_el
5708     = $active_formatting_elements->[$formatting_element_i_in_active - 1]
5709     ->[0];
5710    
5711     ## Step 7
5712     my $node = $furthest_block;
5713     my $node_i_in_open = $furthest_block_i_in_open;
5714     my $last_node = $furthest_block;
5715     S7: {
5716     ## Step 1
5717     $node_i_in_open--;
5718 wakaba 1.3 $node = $self->{open_elements}->[$node_i_in_open];
5719 wakaba 1.1
5720     ## Step 2
5721     my $node_i_in_active;
5722     S7S2: {
5723     for (reverse 0..$#$active_formatting_elements) {
5724     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
5725 wakaba 1.79
5726 wakaba 1.1 $node_i_in_active = $_;
5727     last S7S2;
5728     }
5729     }
5730 wakaba 1.3 splice @{$self->{open_elements}}, $node_i_in_open, 1;
5731 wakaba 1.1 redo S7;
5732     } # S7S2
5733    
5734     ## Step 3
5735     last S7 if $node->[0] eq $formatting_element->[0];
5736    
5737     ## Step 4
5738     if ($last_node->[0] eq $furthest_block->[0]) {
5739 wakaba 1.79
5740 wakaba 1.1 $bookmark_prev_el = $node->[0];
5741     }
5742    
5743     ## Step 5
5744     if ($node->[0]->has_child_nodes ()) {
5745 wakaba 1.79
5746 wakaba 1.1 my $clone = [$node->[0]->clone_node (0), $node->[1]];
5747     $active_formatting_elements->[$node_i_in_active] = $clone;
5748 wakaba 1.3 $self->{open_elements}->[$node_i_in_open] = $clone;
5749 wakaba 1.1 $node = $clone;
5750     }
5751    
5752     ## Step 6
5753     $node->[0]->append_child ($last_node->[0]);
5754    
5755     ## Step 7
5756     $last_node = $node;
5757    
5758     ## Step 8
5759     redo S7;
5760     } # S7
5761    
5762     ## Step 8
5763 wakaba 1.121 if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
5764 wakaba 1.102 my $foster_parent_element;
5765     my $next_sibling;
5766 wakaba 1.121 OE: for (reverse 0..$#{$self->{open_elements}}) {
5767     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5768 wakaba 1.102 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5769     if (defined $parent and $parent->node_type == 1) {
5770    
5771     $foster_parent_element = $parent;
5772     $next_sibling = $self->{open_elements}->[$_]->[0];
5773     } else {
5774    
5775     $foster_parent_element
5776     = $self->{open_elements}->[$_ - 1]->[0];
5777     }
5778     last OE;
5779     }
5780     } # OE
5781     $foster_parent_element = $self->{open_elements}->[0]->[0]
5782     unless defined $foster_parent_element;
5783     $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
5784     $open_tables->[-1]->[1] = 1; # tainted
5785     } else {
5786    
5787     $common_ancestor_node->[0]->append_child ($last_node->[0]);
5788     }
5789 wakaba 1.1
5790     ## Step 9
5791     my $clone = [$formatting_element->[0]->clone_node (0),
5792     $formatting_element->[1]];
5793    
5794     ## Step 10
5795     my @cn = @{$furthest_block->[0]->child_nodes};
5796     $clone->[0]->append_child ($_) for @cn;
5797    
5798     ## Step 11
5799     $furthest_block->[0]->append_child ($clone->[0]);
5800    
5801     ## Step 12
5802     my $i;
5803     AFE: for (reverse 0..$#$active_formatting_elements) {
5804     if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
5805 wakaba 1.79
5806 wakaba 1.1 splice @$active_formatting_elements, $_, 1;
5807     $i-- and last AFE if defined $i;
5808     } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
5809 wakaba 1.79
5810 wakaba 1.1 $i = $_;
5811     }
5812     } # AFE
5813     splice @$active_formatting_elements, $i + 1, 0, $clone;
5814    
5815     ## Step 13
5816     undef $i;
5817 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
5818     if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
5819 wakaba 1.79
5820 wakaba 1.3 splice @{$self->{open_elements}}, $_, 1;
5821 wakaba 1.1 $i-- and last OE if defined $i;
5822 wakaba 1.3 } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
5823 wakaba 1.79
5824 wakaba 1.1 $i = $_;
5825     }
5826     } # OE
5827 wakaba 1.3 splice @{$self->{open_elements}}, $i + 1, 1, $clone;
5828 wakaba 1.1
5829     ## Step 14
5830     redo FET;
5831     } # FET
5832     }; # $formatting_end_tag
5833    
5834 wakaba 1.96 $insert = my $insert_to_current = sub {
5835 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
5836 wakaba 1.1 }; # $insert_to_current
5837    
5838     my $insert_to_foster = sub {
5839 wakaba 1.95 my $child = shift;
5840 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5841 wakaba 1.95 # MUST
5842     my $foster_parent_element;
5843     my $next_sibling;
5844 wakaba 1.121 OE: for (reverse 0..$#{$self->{open_elements}}) {
5845     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5846 wakaba 1.3 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5847 wakaba 1.1 if (defined $parent and $parent->node_type == 1) {
5848 wakaba 1.79
5849 wakaba 1.1 $foster_parent_element = $parent;
5850 wakaba 1.3 $next_sibling = $self->{open_elements}->[$_]->[0];
5851 wakaba 1.1 } else {
5852 wakaba 1.79
5853 wakaba 1.1 $foster_parent_element
5854 wakaba 1.3 = $self->{open_elements}->[$_ - 1]->[0];
5855 wakaba 1.1 }
5856     last OE;
5857     }
5858     } # OE
5859 wakaba 1.3 $foster_parent_element = $self->{open_elements}->[0]->[0]
5860 wakaba 1.1 unless defined $foster_parent_element;
5861     $foster_parent_element->insert_before
5862     ($child, $next_sibling);
5863 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
5864     } else {
5865    
5866     $self->{open_elements}->[-1]->[0]->append_child ($child);
5867     }
5868 wakaba 1.1 }; # $insert_to_foster
5869    
5870 wakaba 1.123 B: while (1) {
5871 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
5872 wakaba 1.79
5873 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in html:#DOCTYPE', token => $token);
5874 wakaba 1.54 ## Ignore the token
5875     ## Stay in the phase
5876     $token = $self->_get_next_token;
5877 wakaba 1.123 next B;
5878 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN and
5879 wakaba 1.54 $token->{tag_name} eq 'html') {
5880 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5881 wakaba 1.79
5882 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html', text => 'html', token => $token);
5883 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
5884     } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5885 wakaba 1.79
5886 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html', text => 'html', token => $token);
5887 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
5888 wakaba 1.79 } else {
5889    
5890 wakaba 1.54 }
5891    
5892 wakaba 1.84
5893 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not first start tag', token => $token);
5894 wakaba 1.54 my $top_el = $self->{open_elements}->[0]->[0];
5895     for my $attr_name (keys %{$token->{attributes}}) {
5896     unless ($top_el->has_attribute_ns (undef, $attr_name)) {
5897 wakaba 1.79
5898 wakaba 1.54 $top_el->set_attribute_ns
5899     (undef, [undef, $attr_name],
5900     $token->{attributes}->{$attr_name}->{value});
5901     }
5902     }
5903 wakaba 1.122
5904 wakaba 1.54 $token = $self->_get_next_token;
5905 wakaba 1.123 next B;
5906 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
5907 wakaba 1.54 my $comment = $self->{document}->create_comment ($token->{data});
5908 wakaba 1.58 if ($self->{insertion_mode} & AFTER_HTML_IMS) {
5909 wakaba 1.79
5910 wakaba 1.54 $self->{document}->append_child ($comment);
5911 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
5912 wakaba 1.79
5913 wakaba 1.54 $self->{open_elements}->[0]->[0]->append_child ($comment);
5914     } else {
5915 wakaba 1.79
5916 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($comment);
5917     }
5918     $token = $self->_get_next_token;
5919 wakaba 1.123 next B;
5920     } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
5921     if ($token->{type} == CHARACTER_TOKEN) {
5922    
5923     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5924     $token = $self->_get_next_token;
5925     next B;
5926     } elsif ($token->{type} == START_TAG_TOKEN) {
5927 wakaba 1.126 if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
5928     $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
5929 wakaba 1.123 not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
5930     ($token->{tag_name} eq 'svg' and
5931     $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
5932     ## NOTE: "using the rules for secondary insertion mode"then"continue"
5933    
5934     #
5935     } elsif ({
5936 wakaba 1.127 b => 1, big => 1, blockquote => 1, body => 1, br => 1,
5937 wakaba 1.143 center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
5938     em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
5939     h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
5940     img => 1, li => 1, listing => 1, menu => 1, meta => 1,
5941     nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
5942     small => 1, span => 1, strong => 1, strike => 1, sub => 1,
5943     sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
5944 wakaba 1.123 }->{$token->{tag_name}}) {
5945    
5946 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
5947     text => $self->{open_elements}->[-1]->[0]
5948 wakaba 1.123 ->manakai_local_name,
5949     token => $token);
5950    
5951     pop @{$self->{open_elements}}
5952     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
5953    
5954 wakaba 1.127 $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
5955 wakaba 1.123 ## Reprocess.
5956     next B;
5957     } else {
5958 wakaba 1.128 my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
5959     my $tag_name = $token->{tag_name};
5960     if ($nsuri eq $SVG_NS) {
5961     $tag_name = {
5962     altglyph => 'altGlyph',
5963     altglyphdef => 'altGlyphDef',
5964     altglyphitem => 'altGlyphItem',
5965     animatecolor => 'animateColor',
5966     animatemotion => 'animateMotion',
5967     animatetransform => 'animateTransform',
5968     clippath => 'clipPath',
5969     feblend => 'feBlend',
5970     fecolormatrix => 'feColorMatrix',
5971     fecomponenttransfer => 'feComponentTransfer',
5972     fecomposite => 'feComposite',
5973     feconvolvematrix => 'feConvolveMatrix',
5974     fediffuselighting => 'feDiffuseLighting',
5975     fedisplacementmap => 'feDisplacementMap',
5976     fedistantlight => 'feDistantLight',
5977     feflood => 'feFlood',
5978     fefunca => 'feFuncA',
5979     fefuncb => 'feFuncB',
5980     fefuncg => 'feFuncG',
5981     fefuncr => 'feFuncR',
5982     fegaussianblur => 'feGaussianBlur',
5983     feimage => 'feImage',
5984     femerge => 'feMerge',
5985     femergenode => 'feMergeNode',
5986     femorphology => 'feMorphology',
5987     feoffset => 'feOffset',
5988     fepointlight => 'fePointLight',
5989     fespecularlighting => 'feSpecularLighting',
5990     fespotlight => 'feSpotLight',
5991     fetile => 'feTile',
5992     feturbulence => 'feTurbulence',
5993     foreignobject => 'foreignObject',
5994     glyphref => 'glyphRef',
5995     lineargradient => 'linearGradient',
5996     radialgradient => 'radialGradient',
5997     #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
5998     textpath => 'textPath',
5999     }->{$tag_name} || $tag_name;
6000     }
6001    
6002     ## "adjust SVG attributes" (SVG only) - done in insert-element-f
6003    
6004     ## "adjust foreign attributes" - done in insert-element-f
6005 wakaba 1.123
6006    
6007     {
6008     my $el;
6009    
6010     $el = $self->{document}->create_element_ns
6011 wakaba 1.128 ($nsuri, [undef, $tag_name]);
6012 wakaba 1.123
6013     for my $attr_name (keys %{ $token->{attributes}}) {
6014     my $attr_t = $token->{attributes}->{$attr_name};
6015 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (
6016     @{
6017     $foreign_attr_xname->{$attr_name} ||
6018     [undef, [undef,
6019 wakaba 1.152 ($nsuri) eq $SVG_NS ?
6020 wakaba 1.128 ($svg_attr_name->{$attr_name} || $attr_name) :
6021 wakaba 1.152 ($nsuri) eq $MML_NS ?
6022     ($attr_name eq 'definitionurl' ?
6023     'definitionURL' : $attr_name) :
6024 wakaba 1.128 $attr_name]]
6025     }
6026     );
6027 wakaba 1.123 $attr->value ($attr_t->{value});
6028     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6029     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6030     $el->set_attribute_node_ns ($attr);
6031     }
6032    
6033     $el->set_user_data (manakai_source_line => $token->{line})
6034     if defined $token->{line};
6035     $el->set_user_data (manakai_source_column => $token->{column})
6036     if defined $token->{column};
6037    
6038     $insert->($el);
6039 wakaba 1.128 push @{$self->{open_elements}}, [$el, ($el_category_f->{$nsuri}->{ $tag_name} || 0) | FOREIGN_EL];
6040    
6041     if ( $token->{attributes}->{xmlns} and $token->{attributes}->{xmlns}->{value} ne ($nsuri)) {
6042 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad namespace', token => $token);
6043 wakaba 1.128 ## TODO: Error type documentation
6044     }
6045 wakaba 1.123 }
6046    
6047    
6048     if ($self->{self_closing}) {
6049     pop @{$self->{open_elements}};
6050     delete $self->{self_closing};
6051     } else {
6052    
6053     }
6054    
6055     $token = $self->_get_next_token;
6056     next B;
6057     }
6058     } elsif ($token->{type} == END_TAG_TOKEN) {
6059     ## NOTE: "using the rules for secondary insertion mode" then "continue"
6060    
6061     #
6062     } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6063    
6064 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
6065     text => $self->{open_elements}->[-1]->[0]
6066 wakaba 1.143 ->manakai_local_name,
6067     token => $token);
6068    
6069     pop @{$self->{open_elements}}
6070     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
6071    
6072     $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
6073     ## Reprocess.
6074     next B;
6075 wakaba 1.123 } else {
6076     die "$0: $token->{type}: Unknown token type";
6077     }
6078     }
6079    
6080     if ($self->{insertion_mode} & HEAD_IMS) {
6081 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
6082 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
6083 wakaba 1.99 unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6084    
6085     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6086 wakaba 1.174 #
6087 wakaba 1.99 } else {
6088    
6089     ## Ignore the token.
6090 wakaba 1.174 #
6091 wakaba 1.99 }
6092 wakaba 1.54 unless (length $token->{data}) {
6093 wakaba 1.79
6094 wakaba 1.54 $token = $self->_get_next_token;
6095 wakaba 1.123 next B;
6096 wakaba 1.54 }
6097 wakaba 1.173 ## TODO: set $token->{column} appropriately
6098 wakaba 1.54 }
6099    
6100 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6101 wakaba 1.79
6102 wakaba 1.54 ## As if <head>
6103    
6104     $self->{head_element} = $self->{document}->create_element_ns
6105 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6106 wakaba 1.54
6107 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6108     if defined $token->{line};
6109     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6110     if defined $token->{column};
6111    
6112 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6113 wakaba 1.121 push @{$self->{open_elements}},
6114     [$self->{head_element}, $el_category->{head}];
6115 wakaba 1.54
6116     ## Reprocess in the "in head" insertion mode...
6117     pop @{$self->{open_elements}};
6118    
6119     ## Reprocess in the "after head" insertion mode...
6120 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6121 wakaba 1.79
6122 wakaba 1.54 ## As if </noscript>
6123     pop @{$self->{open_elements}};
6124 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:#text', token => $token);
6125 wakaba 1.54
6126     ## Reprocess in the "in head" insertion mode...
6127     ## As if </head>
6128     pop @{$self->{open_elements}};
6129    
6130     ## Reprocess in the "after head" insertion mode...
6131 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6132 wakaba 1.79
6133 wakaba 1.54 pop @{$self->{open_elements}};
6134    
6135     ## Reprocess in the "after head" insertion mode...
6136 wakaba 1.79 } else {
6137    
6138 wakaba 1.54 }
6139    
6140 wakaba 1.121 ## "after head" insertion mode
6141     ## As if <body>
6142    
6143 wakaba 1.54 {
6144     my $el;
6145    
6146     $el = $self->{document}->create_element_ns
6147 wakaba 1.128 ($HTML_NS, [undef, 'body']);
6148 wakaba 1.54
6149 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6150     if defined $token->{line};
6151     $el->set_user_data (manakai_source_column => $token->{column})
6152     if defined $token->{column};
6153    
6154 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6155 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
6156 wakaba 1.54 }
6157    
6158 wakaba 1.121 $self->{insertion_mode} = IN_BODY_IM;
6159     ## reprocess
6160 wakaba 1.123 next B;
6161 wakaba 1.121 } elsif ($token->{type} == START_TAG_TOKEN) {
6162     if ($token->{tag_name} eq 'head') {
6163     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6164    
6165    
6166 wakaba 1.54 $self->{head_element} = $self->{document}->create_element_ns
6167 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6168 wakaba 1.54
6169     for my $attr_name (keys %{ $token->{attributes}}) {
6170 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6171 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6172 wakaba 1.117 $attr->value ($attr_t->{value});
6173     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6174     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6175     $self->{head_element}->set_attribute_node_ns ($attr);
6176 wakaba 1.54 }
6177    
6178 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6179     if defined $token->{line};
6180     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6181     if defined $token->{column};
6182    
6183 wakaba 1.121 $self->{open_elements}->[-1]->[0]->append_child
6184     ($self->{head_element});
6185     push @{$self->{open_elements}},
6186     [$self->{head_element}, $el_category->{head}];
6187     $self->{insertion_mode} = IN_HEAD_IM;
6188 wakaba 1.122
6189     $token = $self->_get_next_token;
6190 wakaba 1.123 next B;
6191 wakaba 1.122 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6192    
6193 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head', text => 'head',
6194     token => $token);
6195 wakaba 1.136 ## Ignore the token
6196    
6197     $token = $self->_get_next_token;
6198     next B;
6199 wakaba 1.122 } else {
6200    
6201 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in head:head',
6202     token => $token); # or in head noscript
6203 wakaba 1.122 ## Ignore the token
6204    
6205 wakaba 1.121 $token = $self->_get_next_token;
6206 wakaba 1.123 next B;
6207 wakaba 1.122 }
6208     } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6209 wakaba 1.123
6210     ## As if <head>
6211    
6212 wakaba 1.54 $self->{head_element} = $self->{document}->create_element_ns
6213 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6214 wakaba 1.54
6215 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6216     if defined $token->{line};
6217     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6218     if defined $token->{column};
6219    
6220 wakaba 1.123 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6221     push @{$self->{open_elements}},
6222     [$self->{head_element}, $el_category->{head}];
6223 wakaba 1.54
6224 wakaba 1.123 $self->{insertion_mode} = IN_HEAD_IM;
6225     ## Reprocess in the "in head" insertion mode...
6226     } else {
6227    
6228     }
6229 wakaba 1.54
6230     if ($token->{tag_name} eq 'base') {
6231 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6232 wakaba 1.79
6233 wakaba 1.54 ## As if </noscript>
6234     pop @{$self->{open_elements}};
6235 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'base',
6236     token => $token);
6237 wakaba 1.54
6238 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
6239 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
6240 wakaba 1.79 } else {
6241    
6242 wakaba 1.54 }
6243    
6244     ## NOTE: There is a "as if in head" code clone.
6245 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6246 wakaba 1.79
6247 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6248     text => $token->{tag_name}, token => $token);
6249 wakaba 1.121 push @{$self->{open_elements}},
6250     [$self->{head_element}, $el_category->{head}];
6251 wakaba 1.79 } else {
6252    
6253 wakaba 1.54 }
6254    
6255     {
6256     my $el;
6257    
6258     $el = $self->{document}->create_element_ns
6259 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6260 wakaba 1.54
6261     for my $attr_name (keys %{ $token->{attributes}}) {
6262 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6263 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6264 wakaba 1.117 $attr->value ($attr_t->{value});
6265     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6266     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6267     $el->set_attribute_node_ns ($attr);
6268 wakaba 1.54 }
6269    
6270 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6271     if defined $token->{line};
6272     $el->set_user_data (manakai_source_column => $token->{column})
6273     if defined $token->{column};
6274    
6275 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6276 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6277 wakaba 1.54 }
6278    
6279     pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6280 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6281 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6282 wakaba 1.122
6283 wakaba 1.54 $token = $self->_get_next_token;
6284 wakaba 1.123 next B;
6285 wakaba 1.54 } elsif ($token->{tag_name} eq 'link') {
6286     ## NOTE: There is a "as if in head" code clone.
6287 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6288 wakaba 1.79
6289 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6290     text => $token->{tag_name}, token => $token);
6291 wakaba 1.121 push @{$self->{open_elements}},
6292     [$self->{head_element}, $el_category->{head}];
6293 wakaba 1.79 } else {
6294    
6295 wakaba 1.54 }
6296    
6297 wakaba 1.25 {
6298     my $el;
6299    
6300 wakaba 1.1 $el = $self->{document}->create_element_ns
6301 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6302 wakaba 1.1
6303 wakaba 1.25 for my $attr_name (keys %{ $token->{attributes}}) {
6304 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6305 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6306 wakaba 1.117 $attr->value ($attr_t->{value});
6307     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6308     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6309     $el->set_attribute_node_ns ($attr);
6310 wakaba 1.1 }
6311    
6312 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6313     if defined $token->{line};
6314     $el->set_user_data (manakai_source_column => $token->{column})
6315     if defined $token->{column};
6316    
6317 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6318 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6319 wakaba 1.25 }
6320    
6321 wakaba 1.54 pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6322 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6323 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6324 wakaba 1.122 delete $self->{self_closing};
6325 wakaba 1.54 $token = $self->_get_next_token;
6326 wakaba 1.123 next B;
6327 wakaba 1.54 } elsif ($token->{tag_name} eq 'meta') {
6328     ## NOTE: There is a "as if in head" code clone.
6329 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6330 wakaba 1.79
6331 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6332     text => $token->{tag_name}, token => $token);
6333 wakaba 1.121 push @{$self->{open_elements}},
6334     [$self->{head_element}, $el_category->{head}];
6335 wakaba 1.79 } else {
6336    
6337 wakaba 1.54 }
6338    
6339 wakaba 1.34 {
6340     my $el;
6341    
6342     $el = $self->{document}->create_element_ns
6343 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6344 wakaba 1.34
6345     for my $attr_name (keys %{ $token->{attributes}}) {
6346 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6347 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6348 wakaba 1.117 $attr->value ($attr_t->{value});
6349     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6350     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6351     $el->set_attribute_node_ns ($attr);
6352 wakaba 1.34 }
6353    
6354 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6355     if defined $token->{line};
6356     $el->set_user_data (manakai_source_column => $token->{column})
6357     if defined $token->{column};
6358    
6359 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6360 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6361 wakaba 1.34 }
6362    
6363 wakaba 1.68 my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6364 wakaba 1.34
6365 wakaba 1.54 unless ($self->{confident}) {
6366 wakaba 1.131 if ($token->{attributes}->{charset}) {
6367 wakaba 1.79
6368 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
6369     ## in the {change_encoding} callback.
6370 wakaba 1.65 $self->{change_encoding}
6371 wakaba 1.112 ->($self, $token->{attributes}->{charset}->{value},
6372     $token);
6373 wakaba 1.68
6374     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6375     ->set_user_data (manakai_has_reference =>
6376     $token->{attributes}->{charset}
6377     ->{has_reference});
6378 wakaba 1.65 } elsif ($token->{attributes}->{content}) {
6379     if ($token->{attributes}->{content}->{value}
6380 wakaba 1.141 =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6381 wakaba 1.71 [\x09-\x0D\x20]*=
6382 wakaba 1.54 [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6383 wakaba 1.142 ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {
6384 wakaba 1.79
6385 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
6386     ## in the {change_encoding} callback.
6387 wakaba 1.65 $self->{change_encoding}
6388 wakaba 1.112 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
6389     $token);
6390 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6391     ->set_user_data (manakai_has_reference =>
6392     $token->{attributes}->{content}
6393     ->{has_reference});
6394 wakaba 1.79 } else {
6395    
6396 wakaba 1.65 }
6397 wakaba 1.54 }
6398 wakaba 1.68 } else {
6399     if ($token->{attributes}->{charset}) {
6400 wakaba 1.79
6401 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6402     ->set_user_data (manakai_has_reference =>
6403     $token->{attributes}->{charset}
6404     ->{has_reference});
6405     }
6406 wakaba 1.69 if ($token->{attributes}->{content}) {
6407 wakaba 1.79
6408 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6409     ->set_user_data (manakai_has_reference =>
6410     $token->{attributes}->{content}
6411     ->{has_reference});
6412     }
6413 wakaba 1.54 }
6414 wakaba 1.34
6415 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6416 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6417 wakaba 1.122 delete $self->{self_closing};
6418 wakaba 1.54 $token = $self->_get_next_token;
6419 wakaba 1.123 next B;
6420 wakaba 1.54 } elsif ($token->{tag_name} eq 'title') {
6421 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6422 wakaba 1.79
6423 wakaba 1.54 ## As if </noscript>
6424     pop @{$self->{open_elements}};
6425 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'title',
6426     token => $token);
6427 wakaba 1.1
6428 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
6429 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
6430 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6431 wakaba 1.79
6432 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6433     text => $token->{tag_name}, token => $token);
6434 wakaba 1.121 push @{$self->{open_elements}},
6435     [$self->{head_element}, $el_category->{head}];
6436 wakaba 1.79 } else {
6437    
6438 wakaba 1.54 }
6439    
6440     ## NOTE: There is a "as if in head" code clone.
6441     my $parent = defined $self->{head_element} ? $self->{head_element}
6442     : $self->{open_elements}->[-1]->[0];
6443 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
6444 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6445 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6446 wakaba 1.123 next B;
6447 wakaba 1.145 } elsif ($token->{tag_name} eq 'style' or
6448     $token->{tag_name} eq 'noframes') {
6449 wakaba 1.54 ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
6450 wakaba 1.56 ## insertion mode IN_HEAD_IM)
6451 wakaba 1.54 ## NOTE: There is a "as if in head" code clone.
6452 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6453 wakaba 1.79
6454 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6455     text => $token->{tag_name}, token => $token);
6456 wakaba 1.121 push @{$self->{open_elements}},
6457     [$self->{head_element}, $el_category->{head}];
6458 wakaba 1.79 } else {
6459    
6460 wakaba 1.54 }
6461 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
6462 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6463 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6464 wakaba 1.123 next B;
6465 wakaba 1.54 } elsif ($token->{tag_name} eq 'noscript') {
6466 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_IM) {
6467 wakaba 1.79
6468 wakaba 1.54 ## NOTE: and scripting is disalbed
6469    
6470 wakaba 1.1 {
6471     my $el;
6472    
6473     $el = $self->{document}->create_element_ns
6474 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6475 wakaba 1.1
6476     for my $attr_name (keys %{ $token->{attributes}}) {
6477 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6478 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6479 wakaba 1.117 $attr->value ($attr_t->{value});
6480     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6481     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6482     $el->set_attribute_node_ns ($attr);
6483 wakaba 1.1 }
6484    
6485 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6486     if defined $token->{line};
6487     $el->set_user_data (manakai_source_column => $token->{column})
6488     if defined $token->{column};
6489    
6490 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6491 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6492 wakaba 1.1 }
6493    
6494 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
6495 wakaba 1.122
6496 wakaba 1.54 $token = $self->_get_next_token;
6497 wakaba 1.123 next B;
6498 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6499 wakaba 1.79
6500 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'noscript',
6501     token => $token);
6502 wakaba 1.54 ## Ignore the token
6503 wakaba 1.122
6504 wakaba 1.54 $token = $self->_get_next_token;
6505 wakaba 1.123 next B;
6506 wakaba 1.54 } else {
6507 wakaba 1.79
6508 wakaba 1.54 #
6509     }
6510     } elsif ($token->{tag_name} eq 'script') {
6511 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6512 wakaba 1.79
6513 wakaba 1.54 ## As if </noscript>
6514     pop @{$self->{open_elements}};
6515 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'script',
6516     token => $token);
6517 wakaba 1.54
6518 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
6519 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
6520 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6521 wakaba 1.79
6522 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6523     text => $token->{tag_name}, token => $token);
6524 wakaba 1.121 push @{$self->{open_elements}},
6525     [$self->{head_element}, $el_category->{head}];
6526 wakaba 1.79 } else {
6527    
6528 wakaba 1.54 }
6529    
6530     ## NOTE: There is a "as if in head" code clone.
6531 wakaba 1.100 $script_start_tag->();
6532     pop @{$self->{open_elements}} # <head>
6533 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6534 wakaba 1.123 next B;
6535 wakaba 1.54 } elsif ($token->{tag_name} eq 'body' or
6536     $token->{tag_name} eq 'frameset') {
6537 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6538 wakaba 1.79
6539 wakaba 1.54 ## As if </noscript>
6540     pop @{$self->{open_elements}};
6541 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript',
6542     text => $token->{tag_name}, token => $token);
6543 wakaba 1.54
6544     ## Reprocess in the "in head" insertion mode...
6545     ## As if </head>
6546     pop @{$self->{open_elements}};
6547    
6548     ## Reprocess in the "after head" insertion mode...
6549 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6550 wakaba 1.79
6551 wakaba 1.54 pop @{$self->{open_elements}};
6552    
6553     ## Reprocess in the "after head" insertion mode...
6554 wakaba 1.79 } else {
6555    
6556 wakaba 1.54 }
6557    
6558     ## "after head" insertion mode
6559    
6560 wakaba 1.1 {
6561     my $el;
6562    
6563     $el = $self->{document}->create_element_ns
6564 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6565 wakaba 1.1
6566     for my $attr_name (keys %{ $token->{attributes}}) {
6567 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6568 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6569 wakaba 1.117 $attr->value ($attr_t->{value});
6570     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6571     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6572     $el->set_attribute_node_ns ($attr);
6573 wakaba 1.1 }
6574    
6575 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6576     if defined $token->{line};
6577     $el->set_user_data (manakai_source_column => $token->{column})
6578     if defined $token->{column};
6579    
6580 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6581 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6582 wakaba 1.1 }
6583    
6584 wakaba 1.56 if ($token->{tag_name} eq 'body') {
6585 wakaba 1.79
6586 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
6587     } elsif ($token->{tag_name} eq 'frameset') {
6588 wakaba 1.79
6589 wakaba 1.56 $self->{insertion_mode} = IN_FRAMESET_IM;
6590     } else {
6591     die "$0: tag name: $self->{tag_name}";
6592     }
6593 wakaba 1.122
6594 wakaba 1.54 $token = $self->_get_next_token;
6595 wakaba 1.123 next B;
6596 wakaba 1.54 } else {
6597 wakaba 1.79
6598 wakaba 1.54 #
6599 wakaba 1.8 }
6600 wakaba 1.54
6601 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6602 wakaba 1.79
6603 wakaba 1.54 ## As if </noscript>
6604     pop @{$self->{open_elements}};
6605 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
6606     text => $token->{tag_name}, token => $token);
6607 wakaba 1.54
6608     ## Reprocess in the "in head" insertion mode...
6609     ## As if </head>
6610     pop @{$self->{open_elements}};
6611    
6612     ## Reprocess in the "after head" insertion mode...
6613 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6614 wakaba 1.79
6615 wakaba 1.54 ## As if </head>
6616     pop @{$self->{open_elements}};
6617    
6618     ## Reprocess in the "after head" insertion mode...
6619 wakaba 1.79 } else {
6620    
6621 wakaba 1.54 }
6622    
6623     ## "after head" insertion mode
6624     ## As if <body>
6625    
6626 wakaba 1.1 {
6627     my $el;
6628    
6629     $el = $self->{document}->create_element_ns
6630 wakaba 1.128 ($HTML_NS, [undef, 'body']);
6631 wakaba 1.1
6632 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6633     if defined $token->{line};
6634     $el->set_user_data (manakai_source_column => $token->{column})
6635     if defined $token->{column};
6636    
6637 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6638 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
6639 wakaba 1.1 }
6640    
6641 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
6642 wakaba 1.54 ## reprocess
6643 wakaba 1.122
6644 wakaba 1.123 next B;
6645 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
6646 wakaba 1.54 if ($token->{tag_name} eq 'head') {
6647 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6648 wakaba 1.79
6649 wakaba 1.54 ## As if <head>
6650    
6651     $self->{head_element} = $self->{document}->create_element_ns
6652 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6653 wakaba 1.54
6654 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6655     if defined $token->{line};
6656     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6657     if defined $token->{column};
6658    
6659 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6660 wakaba 1.121 push @{$self->{open_elements}},
6661     [$self->{head_element}, $el_category->{head}];
6662 wakaba 1.54
6663     ## Reprocess in the "in head" insertion mode...
6664     pop @{$self->{open_elements}};
6665 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
6666 wakaba 1.54 $token = $self->_get_next_token;
6667 wakaba 1.123 next B;
6668 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6669 wakaba 1.79
6670 wakaba 1.54 ## As if </noscript>
6671     pop @{$self->{open_elements}};
6672 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
6673     text => 'head', token => $token);
6674 wakaba 1.54
6675     ## Reprocess in the "in head" insertion mode...
6676     pop @{$self->{open_elements}};
6677 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
6678 wakaba 1.54 $token = $self->_get_next_token;
6679 wakaba 1.123 next B;
6680 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6681 wakaba 1.79
6682 wakaba 1.54 pop @{$self->{open_elements}};
6683 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
6684 wakaba 1.54 $token = $self->_get_next_token;
6685 wakaba 1.123 next B;
6686 wakaba 1.136 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6687    
6688 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => 'head',
6689     token => $token);
6690 wakaba 1.136 ## Ignore the token
6691     $token = $self->_get_next_token;
6692     next B;
6693 wakaba 1.54 } else {
6694 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
6695 wakaba 1.54 }
6696     } elsif ($token->{tag_name} eq 'noscript') {
6697 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6698 wakaba 1.79
6699 wakaba 1.54 pop @{$self->{open_elements}};
6700 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
6701 wakaba 1.54 $token = $self->_get_next_token;
6702 wakaba 1.123 next B;
6703 wakaba 1.136 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
6704     $self->{insertion_mode} == AFTER_HEAD_IM) {
6705 wakaba 1.79
6706 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6707     text => 'noscript', token => $token);
6708 wakaba 1.54 ## Ignore the token ## ISSUE: An issue in the spec.
6709     $token = $self->_get_next_token;
6710 wakaba 1.123 next B;
6711 wakaba 1.54 } else {
6712 wakaba 1.79
6713 wakaba 1.54 #
6714     }
6715     } elsif ({
6716     body => 1, html => 1,
6717     }->{$token->{tag_name}}) {
6718 wakaba 1.136 if ($self->{insertion_mode} == BEFORE_HEAD_IM or
6719     $self->{insertion_mode} == IN_HEAD_IM or
6720     $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6721 wakaba 1.79
6722 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6723     text => $token->{tag_name}, token => $token);
6724 wakaba 1.136 ## Ignore the token
6725     $token = $self->_get_next_token;
6726     next B;
6727     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6728 wakaba 1.79
6729 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6730     text => $token->{tag_name}, token => $token);
6731 wakaba 1.54 ## Ignore the token
6732     $token = $self->_get_next_token;
6733 wakaba 1.123 next B;
6734 wakaba 1.79 } else {
6735 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
6736 wakaba 1.54 }
6737 wakaba 1.136 } elsif ($token->{tag_name} eq 'p') {
6738    
6739 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6740     text => $token->{tag_name}, token => $token);
6741 wakaba 1.136 ## Ignore the token
6742     $token = $self->_get_next_token;
6743     next B;
6744     } elsif ($token->{tag_name} eq 'br') {
6745 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6746 wakaba 1.79
6747 wakaba 1.136 ## (before head) as if <head>, (in head) as if </head>
6748 wakaba 1.54
6749     $self->{head_element} = $self->{document}->create_element_ns
6750 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6751 wakaba 1.54
6752 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6753     if defined $token->{line};
6754     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6755     if defined $token->{column};
6756    
6757 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6758 wakaba 1.136 $self->{insertion_mode} = AFTER_HEAD_IM;
6759    
6760     ## Reprocess in the "after head" insertion mode...
6761     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6762    
6763     ## As if </head>
6764     pop @{$self->{open_elements}};
6765     $self->{insertion_mode} = AFTER_HEAD_IM;
6766    
6767     ## Reprocess in the "after head" insertion mode...
6768     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6769    
6770     ## ISSUE: Two parse errors for <head><noscript></br>
6771 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6772     text => 'br', token => $token);
6773 wakaba 1.136 ## As if </noscript>
6774     pop @{$self->{open_elements}};
6775     $self->{insertion_mode} = IN_HEAD_IM;
6776 wakaba 1.54
6777     ## Reprocess in the "in head" insertion mode...
6778 wakaba 1.136 ## As if </head>
6779     pop @{$self->{open_elements}};
6780     $self->{insertion_mode} = AFTER_HEAD_IM;
6781 wakaba 1.54
6782 wakaba 1.136 ## Reprocess in the "after head" insertion mode...
6783     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6784 wakaba 1.79
6785 wakaba 1.56 #
6786     } else {
6787 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
6788 wakaba 1.54 }
6789 wakaba 1.136
6790     ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
6791 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6792     text => 'br', token => $token);
6793 wakaba 1.136 ## Ignore the token
6794     $token = $self->_get_next_token;
6795     next B;
6796     } else {
6797    
6798 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6799     text => $token->{tag_name}, token => $token);
6800 wakaba 1.136 ## Ignore the token
6801     $token = $self->_get_next_token;
6802     next B;
6803 wakaba 1.54 }
6804    
6805 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6806 wakaba 1.79
6807 wakaba 1.54 ## As if </noscript>
6808     pop @{$self->{open_elements}};
6809 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
6810     text => $token->{tag_name}, token => $token);
6811 wakaba 1.54
6812     ## Reprocess in the "in head" insertion mode...
6813     ## As if </head>
6814     pop @{$self->{open_elements}};
6815    
6816     ## Reprocess in the "after head" insertion mode...
6817 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6818 wakaba 1.79
6819 wakaba 1.54 ## As if </head>
6820     pop @{$self->{open_elements}};
6821    
6822     ## Reprocess in the "after head" insertion mode...
6823 wakaba 1.56 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6824 wakaba 1.82 ## ISSUE: This case cannot be reached?
6825 wakaba 1.79
6826 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6827     text => $token->{tag_name}, token => $token);
6828 wakaba 1.54 ## Ignore the token ## ISSUE: An issue in the spec.
6829     $token = $self->_get_next_token;
6830 wakaba 1.123 next B;
6831 wakaba 1.79 } else {
6832    
6833 wakaba 1.8 }
6834 wakaba 1.54
6835     ## "after head" insertion mode
6836     ## As if <body>
6837    
6838 wakaba 1.1 {
6839     my $el;
6840    
6841     $el = $self->{document}->create_element_ns
6842 wakaba 1.128 ($HTML_NS, [undef, 'body']);
6843 wakaba 1.1
6844 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6845     if defined $token->{line};
6846     $el->set_user_data (manakai_source_column => $token->{column})
6847     if defined $token->{column};
6848    
6849 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6850 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
6851 wakaba 1.1 }
6852    
6853 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
6854 wakaba 1.54 ## reprocess
6855 wakaba 1.123 next B;
6856 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6857     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6858    
6859    
6860     ## NOTE: As if <head>
6861    
6862     $self->{head_element} = $self->{document}->create_element_ns
6863 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6864 wakaba 1.103
6865 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6866     if defined $token->{line};
6867     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6868     if defined $token->{column};
6869    
6870 wakaba 1.103 $self->{open_elements}->[-1]->[0]->append_child
6871     ($self->{head_element});
6872 wakaba 1.121 #push @{$self->{open_elements}},
6873     # [$self->{head_element}, $el_category->{head}];
6874 wakaba 1.103 #$self->{insertion_mode} = IN_HEAD_IM;
6875     ## NOTE: Reprocess.
6876    
6877     ## NOTE: As if </head>
6878     #pop @{$self->{open_elements}};
6879     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
6880     ## NOTE: Reprocess.
6881    
6882     #
6883     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6884    
6885    
6886     ## NOTE: As if </head>
6887     pop @{$self->{open_elements}};
6888     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
6889     ## NOTE: Reprocess.
6890    
6891     #
6892     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6893    
6894    
6895 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:#eof', token => $token);
6896 wakaba 1.103
6897     ## As if </noscript>
6898     pop @{$self->{open_elements}};
6899     #$self->{insertion_mode} = IN_HEAD_IM;
6900     ## NOTE: Reprocess.
6901    
6902     ## NOTE: As if </head>
6903     pop @{$self->{open_elements}};
6904     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
6905     ## NOTE: Reprocess.
6906    
6907     #
6908     } else {
6909    
6910     #
6911     }
6912    
6913     ## NOTE: As if <body>
6914    
6915     {
6916     my $el;
6917    
6918     $el = $self->{document}->create_element_ns
6919 wakaba 1.128 ($HTML_NS, [undef, 'body']);
6920 wakaba 1.103
6921 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6922     if defined $token->{line};
6923     $el->set_user_data (manakai_source_column => $token->{column})
6924     if defined $token->{column};
6925    
6926 wakaba 1.103 $self->{open_elements}->[-1]->[0]->append_child ($el);
6927 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
6928 wakaba 1.103 }
6929    
6930     $self->{insertion_mode} = IN_BODY_IM;
6931     ## NOTE: Reprocess.
6932 wakaba 1.123 next B;
6933 wakaba 1.103 } else {
6934     die "$0: $token->{type}: Unknown token type";
6935     }
6936 wakaba 1.54
6937     ## ISSUE: An issue in the spec.
6938 wakaba 1.58 } elsif ($self->{insertion_mode} & BODY_IMS) {
6939 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
6940 wakaba 1.79
6941 wakaba 1.54 ## NOTE: There is a code clone of "character in body".
6942     $reconstruct_active_formatting_elements->($insert_to_current);
6943    
6944     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6945    
6946     $token = $self->_get_next_token;
6947 wakaba 1.123 next B;
6948 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
6949 wakaba 1.54 if ({
6950     caption => 1, col => 1, colgroup => 1, tbody => 1,
6951     td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
6952     }->{$token->{tag_name}}) {
6953 wakaba 1.56 if ($self->{insertion_mode} == IN_CELL_IM) {
6954 wakaba 1.54 ## have an element in table scope
6955 wakaba 1.106 for (reverse 0..$#{$self->{open_elements}}) {
6956 wakaba 1.54 my $node = $self->{open_elements}->[$_];
6957 wakaba 1.121 if ($node->[1] & TABLE_CELL_EL) {
6958 wakaba 1.79
6959 wakaba 1.106
6960     ## Close the cell
6961 wakaba 1.122
6962     $token->{self_closing} = $self->{self_closing};
6963     unshift @{$self->{token}}, $token;
6964     delete $self->{self_closing};
6965     # <x>
6966 wakaba 1.120 $token = {type => END_TAG_TOKEN,
6967     tag_name => $node->[0]->manakai_local_name,
6968 wakaba 1.112 line => $token->{line},
6969     column => $token->{column}};
6970 wakaba 1.123 next B;
6971 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6972 wakaba 1.79
6973 wakaba 1.106 ## ISSUE: This case can never be reached, maybe.
6974     last;
6975 wakaba 1.54 }
6976 wakaba 1.106 }
6977    
6978 wakaba 1.54
6979 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'start tag not allowed',
6980     text => $token->{tag_name}, token => $token);
6981 wakaba 1.106 ## Ignore the token
6982 wakaba 1.122
6983 wakaba 1.106 $token = $self->_get_next_token;
6984 wakaba 1.123 next B;
6985 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
6986 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'caption',
6987     token => $token);
6988 wakaba 1.54
6989 wakaba 1.106 ## NOTE: As if </caption>.
6990 wakaba 1.54 ## have a table element in table scope
6991     my $i;
6992 wakaba 1.106 INSCOPE: {
6993     for (reverse 0..$#{$self->{open_elements}}) {
6994     my $node = $self->{open_elements}->[$_];
6995 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
6996 wakaba 1.106
6997     $i = $_;
6998     last INSCOPE;
6999 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7000 wakaba 1.106
7001     last;
7002     }
7003 wakaba 1.54 }
7004 wakaba 1.106
7005    
7006 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'start tag not allowed',
7007     text => $token->{tag_name}, token => $token);
7008 wakaba 1.106 ## Ignore the token
7009 wakaba 1.122
7010 wakaba 1.106 $token = $self->_get_next_token;
7011 wakaba 1.123 next B;
7012 wakaba 1.54 } # INSCOPE
7013    
7014     ## generate implied end tags
7015 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7016     & END_TAG_OPTIONAL_EL) {
7017 wakaba 1.79
7018 wakaba 1.86 pop @{$self->{open_elements}};
7019 wakaba 1.54 }
7020    
7021 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7022 wakaba 1.79
7023 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7024     text => $self->{open_elements}->[-1]->[0]
7025 wakaba 1.120 ->manakai_local_name,
7026     token => $token);
7027 wakaba 1.79 } else {
7028    
7029 wakaba 1.54 }
7030    
7031     splice @{$self->{open_elements}}, $i;
7032    
7033     $clear_up_to_marker->();
7034    
7035 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7036 wakaba 1.54
7037     ## reprocess
7038 wakaba 1.122
7039 wakaba 1.123 next B;
7040 wakaba 1.54 } else {
7041 wakaba 1.79
7042 wakaba 1.54 #
7043     }
7044     } else {
7045 wakaba 1.79
7046 wakaba 1.54 #
7047     }
7048 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
7049 wakaba 1.54 if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
7050 wakaba 1.56 if ($self->{insertion_mode} == IN_CELL_IM) {
7051 wakaba 1.54 ## have an element in table scope
7052     my $i;
7053     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7054     my $node = $self->{open_elements}->[$_];
7055 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7056 wakaba 1.79
7057 wakaba 1.54 $i = $_;
7058     last INSCOPE;
7059 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7060 wakaba 1.79
7061 wakaba 1.54 last INSCOPE;
7062     }
7063     } # INSCOPE
7064     unless (defined $i) {
7065 wakaba 1.79
7066 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7067     text => $token->{tag_name},
7068     token => $token);
7069 wakaba 1.54 ## Ignore the token
7070     $token = $self->_get_next_token;
7071 wakaba 1.123 next B;
7072 wakaba 1.54 }
7073    
7074     ## generate implied end tags
7075 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7076     & END_TAG_OPTIONAL_EL) {
7077 wakaba 1.79
7078 wakaba 1.86 pop @{$self->{open_elements}};
7079 wakaba 1.54 }
7080 wakaba 1.86
7081 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7082     ne $token->{tag_name}) {
7083 wakaba 1.79
7084 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7085     text => $self->{open_elements}->[-1]->[0]
7086 wakaba 1.120 ->manakai_local_name,
7087     token => $token);
7088 wakaba 1.79 } else {
7089    
7090 wakaba 1.54 }
7091    
7092     splice @{$self->{open_elements}}, $i;
7093    
7094     $clear_up_to_marker->();
7095    
7096 wakaba 1.56 $self->{insertion_mode} = IN_ROW_IM;
7097 wakaba 1.54
7098     $token = $self->_get_next_token;
7099 wakaba 1.123 next B;
7100 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
7101 wakaba 1.79
7102 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7103     text => $token->{tag_name}, token => $token);
7104 wakaba 1.54 ## Ignore the token
7105     $token = $self->_get_next_token;
7106 wakaba 1.123 next B;
7107 wakaba 1.54 } else {
7108 wakaba 1.79
7109 wakaba 1.54 #
7110     }
7111     } elsif ($token->{tag_name} eq 'caption') {
7112 wakaba 1.56 if ($self->{insertion_mode} == IN_CAPTION_IM) {
7113 wakaba 1.54 ## have a table element in table scope
7114     my $i;
7115 wakaba 1.106 INSCOPE: {
7116     for (reverse 0..$#{$self->{open_elements}}) {
7117     my $node = $self->{open_elements}->[$_];
7118 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
7119 wakaba 1.106
7120     $i = $_;
7121     last INSCOPE;
7122 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7123 wakaba 1.106
7124     last;
7125     }
7126 wakaba 1.54 }
7127 wakaba 1.106
7128    
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.106 ## Ignore the token
7132     $token = $self->_get_next_token;
7133 wakaba 1.123 next B;
7134 wakaba 1.54 } # INSCOPE
7135    
7136     ## generate implied end tags
7137 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7138     & END_TAG_OPTIONAL_EL) {
7139 wakaba 1.79
7140 wakaba 1.86 pop @{$self->{open_elements}};
7141 wakaba 1.54 }
7142    
7143 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7144 wakaba 1.79
7145 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7146     text => $self->{open_elements}->[-1]->[0]
7147 wakaba 1.120 ->manakai_local_name,
7148     token => $token);
7149 wakaba 1.79 } else {
7150    
7151 wakaba 1.54 }
7152    
7153     splice @{$self->{open_elements}}, $i;
7154    
7155     $clear_up_to_marker->();
7156    
7157 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7158 wakaba 1.54
7159     $token = $self->_get_next_token;
7160 wakaba 1.123 next B;
7161 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CELL_IM) {
7162 wakaba 1.79
7163 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7164     text => $token->{tag_name}, token => $token);
7165 wakaba 1.54 ## Ignore the token
7166     $token = $self->_get_next_token;
7167 wakaba 1.123 next B;
7168 wakaba 1.54 } else {
7169 wakaba 1.79
7170 wakaba 1.54 #
7171 wakaba 1.1 }
7172 wakaba 1.54 } elsif ({
7173     table => 1, tbody => 1, tfoot => 1,
7174     thead => 1, tr => 1,
7175     }->{$token->{tag_name}} and
7176 wakaba 1.56 $self->{insertion_mode} == IN_CELL_IM) {
7177 wakaba 1.54 ## have an element in table scope
7178     my $i;
7179     my $tn;
7180 wakaba 1.106 INSCOPE: {
7181     for (reverse 0..$#{$self->{open_elements}}) {
7182     my $node = $self->{open_elements}->[$_];
7183 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7184 wakaba 1.106
7185     $i = $_;
7186    
7187     ## Close the cell
7188 wakaba 1.122
7189     $token->{self_closing} = $self->{self_closing};
7190     unshift @{$self->{token}}, $token;
7191     delete $self->{self_closing};
7192     # </x>
7193 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => $tn,
7194     line => $token->{line},
7195     column => $token->{column}};
7196 wakaba 1.123 next B;
7197 wakaba 1.121 } elsif ($node->[1] & TABLE_CELL_EL) {
7198 wakaba 1.106
7199 wakaba 1.121 $tn = $node->[0]->manakai_local_name;
7200 wakaba 1.106 ## NOTE: There is exactly one |td| or |th| element
7201     ## in scope in the stack of open elements by definition.
7202 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7203 wakaba 1.106 ## ISSUE: Can this be reached?
7204    
7205     last;
7206     }
7207 wakaba 1.54 }
7208 wakaba 1.106
7209 wakaba 1.79
7210 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7211     text => $token->{tag_name}, token => $token);
7212 wakaba 1.54 ## Ignore the token
7213     $token = $self->_get_next_token;
7214 wakaba 1.123 next B;
7215 wakaba 1.106 } # INSCOPE
7216 wakaba 1.54 } elsif ($token->{tag_name} eq 'table' and
7217 wakaba 1.56 $self->{insertion_mode} == IN_CAPTION_IM) {
7218 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'caption',
7219     token => $token);
7220 wakaba 1.31
7221 wakaba 1.54 ## As if </caption>
7222     ## have a table element in table scope
7223     my $i;
7224     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7225     my $node = $self->{open_elements}->[$_];
7226 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
7227 wakaba 1.79
7228 wakaba 1.54 $i = $_;
7229     last INSCOPE;
7230 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7231 wakaba 1.79
7232 wakaba 1.54 last INSCOPE;
7233     }
7234     } # INSCOPE
7235     unless (defined $i) {
7236 wakaba 1.79
7237 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7238     text => 'caption', token => $token);
7239 wakaba 1.54 ## Ignore the token
7240     $token = $self->_get_next_token;
7241 wakaba 1.123 next B;
7242 wakaba 1.54 }
7243    
7244     ## generate implied end tags
7245 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7246 wakaba 1.79
7247 wakaba 1.86 pop @{$self->{open_elements}};
7248 wakaba 1.54 }
7249 wakaba 1.20
7250 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7251 wakaba 1.79
7252 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7253     text => $self->{open_elements}->[-1]->[0]
7254 wakaba 1.120 ->manakai_local_name,
7255     token => $token);
7256 wakaba 1.79 } else {
7257    
7258 wakaba 1.54 }
7259 wakaba 1.12
7260 wakaba 1.54 splice @{$self->{open_elements}}, $i;
7261 wakaba 1.31
7262 wakaba 1.54 $clear_up_to_marker->();
7263 wakaba 1.1
7264 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7265 wakaba 1.3
7266 wakaba 1.54 ## reprocess
7267 wakaba 1.123 next B;
7268 wakaba 1.54 } elsif ({
7269     body => 1, col => 1, colgroup => 1, html => 1,
7270     }->{$token->{tag_name}}) {
7271 wakaba 1.58 if ($self->{insertion_mode} & BODY_TABLE_IMS) {
7272 wakaba 1.79
7273 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7274     text => $token->{tag_name}, token => $token);
7275 wakaba 1.54 ## Ignore the token
7276     $token = $self->_get_next_token;
7277 wakaba 1.123 next B;
7278 wakaba 1.54 } else {
7279 wakaba 1.79
7280 wakaba 1.54 #
7281     }
7282     } elsif ({
7283     tbody => 1, tfoot => 1,
7284     thead => 1, tr => 1,
7285     }->{$token->{tag_name}} and
7286 wakaba 1.56 $self->{insertion_mode} == IN_CAPTION_IM) {
7287 wakaba 1.79
7288 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7289     text => $token->{tag_name}, token => $token);
7290 wakaba 1.1 ## Ignore the token
7291     $token = $self->_get_next_token;
7292 wakaba 1.123 next B;
7293 wakaba 1.54 } else {
7294 wakaba 1.79
7295 wakaba 1.54 #
7296 wakaba 1.1 }
7297 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7298     for my $entry (@{$self->{open_elements}}) {
7299 wakaba 1.121 unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
7300 wakaba 1.103
7301 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
7302 wakaba 1.103 last;
7303     }
7304     }
7305    
7306     ## Stop parsing.
7307     last B;
7308 wakaba 1.54 } else {
7309     die "$0: $token->{type}: Unknown token type";
7310 wakaba 1.1 }
7311    
7312 wakaba 1.54 $insert = $insert_to_current;
7313     #
7314 wakaba 1.58 } elsif ($self->{insertion_mode} & TABLE_IMS) {
7315 wakaba 1.60 if ($token->{type} == CHARACTER_TOKEN) {
7316 wakaba 1.95 if (not $open_tables->[-1]->[1] and # tainted
7317     $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
7318     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
7319 wakaba 1.54
7320 wakaba 1.95 unless (length $token->{data}) {
7321    
7322     $token = $self->_get_next_token;
7323 wakaba 1.123 next B;
7324 wakaba 1.95 } else {
7325    
7326     }
7327     }
7328 wakaba 1.1
7329 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table:#text', token => $token);
7330 wakaba 1.1
7331 wakaba 1.54 ## As if in body, but insert into foster parent element
7332     ## ISSUE: Spec says that "whenever a node would be inserted
7333     ## into the current node" while characters might not be
7334     ## result in a new Text node.
7335     $reconstruct_active_formatting_elements->($insert_to_foster);
7336    
7337 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
7338 wakaba 1.54 # MUST
7339     my $foster_parent_element;
7340     my $next_sibling;
7341     my $prev_sibling;
7342     OE: for (reverse 0..$#{$self->{open_elements}}) {
7343 wakaba 1.121 if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
7344 wakaba 1.54 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
7345     if (defined $parent and $parent->node_type == 1) {
7346 wakaba 1.79
7347 wakaba 1.54 $foster_parent_element = $parent;
7348     $next_sibling = $self->{open_elements}->[$_]->[0];
7349     $prev_sibling = $next_sibling->previous_sibling;
7350     } else {
7351 wakaba 1.79
7352 wakaba 1.54 $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
7353     $prev_sibling = $foster_parent_element->last_child;
7354     }
7355     last OE;
7356     }
7357     } # OE
7358     $foster_parent_element = $self->{open_elements}->[0]->[0] and
7359     $prev_sibling = $foster_parent_element->last_child
7360     unless defined $foster_parent_element;
7361     if (defined $prev_sibling and
7362     $prev_sibling->node_type == 3) {
7363 wakaba 1.79
7364 wakaba 1.54 $prev_sibling->manakai_append_text ($token->{data});
7365     } else {
7366 wakaba 1.79
7367 wakaba 1.54 $foster_parent_element->insert_before
7368     ($self->{document}->create_text_node ($token->{data}),
7369     $next_sibling);
7370     }
7371 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
7372     } else {
7373    
7374     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
7375     }
7376 wakaba 1.54
7377 wakaba 1.95 $token = $self->_get_next_token;
7378 wakaba 1.123 next B;
7379 wakaba 1.60 } elsif ($token->{type} == START_TAG_TOKEN) {
7380 wakaba 1.150 if ({
7381     tr => ($self->{insertion_mode} != IN_ROW_IM),
7382     th => 1, td => 1,
7383     }->{$token->{tag_name}}) {
7384     if ($self->{insertion_mode} == IN_TABLE_IM) {
7385     ## Clear back to table context
7386     while (not ($self->{open_elements}->[-1]->[1]
7387     & TABLE_SCOPING_EL)) {
7388    
7389     pop @{$self->{open_elements}};
7390     }
7391    
7392    
7393 wakaba 1.54 {
7394     my $el;
7395    
7396     $el = $self->{document}->create_element_ns
7397 wakaba 1.128 ($HTML_NS, [undef, 'tbody']);
7398 wakaba 1.54
7399 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7400     if defined $token->{line};
7401     $el->set_user_data (manakai_source_column => $token->{column})
7402     if defined $token->{column};
7403    
7404 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7405 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'tbody'} || 0];
7406 wakaba 1.54 }
7407    
7408 wakaba 1.150 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7409     ## reprocess in the "in table body" insertion mode...
7410     }
7411    
7412     if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
7413     unless ($token->{tag_name} eq 'tr') {
7414    
7415     $self->{parse_error}->(level => $self->{level}->{must}, type => 'missing start tag:tr', token => $token);
7416     }
7417 wakaba 1.54
7418 wakaba 1.150 ## Clear back to table body context
7419     while (not ($self->{open_elements}->[-1]->[1]
7420     & TABLE_ROWS_SCOPING_EL)) {
7421    
7422     ## ISSUE: Can this case be reached?
7423     pop @{$self->{open_elements}};
7424     }
7425 wakaba 1.54
7426 wakaba 1.56 $self->{insertion_mode} = IN_ROW_IM;
7427 wakaba 1.54 if ($token->{tag_name} eq 'tr') {
7428    
7429 wakaba 1.79
7430 wakaba 1.54 {
7431     my $el;
7432    
7433     $el = $self->{document}->create_element_ns
7434 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7435 wakaba 1.54
7436     for my $attr_name (keys %{ $token->{attributes}}) {
7437 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7438 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7439 wakaba 1.117 $attr->value ($attr_t->{value});
7440     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7441     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7442     $el->set_attribute_node_ns ($attr);
7443 wakaba 1.1 }
7444 wakaba 1.54
7445 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7446     if defined $token->{line};
7447     $el->set_user_data (manakai_source_column => $token->{column})
7448     if defined $token->{column};
7449    
7450 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7451 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7452 wakaba 1.54 }
7453    
7454 wakaba 1.122
7455 wakaba 1.54 $token = $self->_get_next_token;
7456 wakaba 1.123 next B;
7457 wakaba 1.54 } else {
7458    
7459 wakaba 1.79
7460 wakaba 1.54 {
7461     my $el;
7462    
7463     $el = $self->{document}->create_element_ns
7464 wakaba 1.128 ($HTML_NS, [undef, 'tr']);
7465 wakaba 1.54
7466 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7467     if defined $token->{line};
7468     $el->set_user_data (manakai_source_column => $token->{column})
7469     if defined $token->{column};
7470    
7471 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7472 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'tr'} || 0];
7473 wakaba 1.54 }
7474    
7475     ## reprocess in the "in row" insertion mode
7476     }
7477 wakaba 1.79 } else {
7478    
7479 wakaba 1.54 }
7480 wakaba 1.52
7481 wakaba 1.54 ## Clear back to table row context
7482 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7483     & TABLE_ROW_SCOPING_EL)) {
7484 wakaba 1.79
7485 wakaba 1.54 pop @{$self->{open_elements}};
7486     }
7487    
7488    
7489     {
7490     my $el;
7491    
7492     $el = $self->{document}->create_element_ns
7493 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7494 wakaba 1.1
7495 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
7496 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7497 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7498 wakaba 1.117 $attr->value ($attr_t->{value});
7499     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7500     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7501     $el->set_attribute_node_ns ($attr);
7502 wakaba 1.54 }
7503    
7504 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7505     if defined $token->{line};
7506     $el->set_user_data (manakai_source_column => $token->{column})
7507     if defined $token->{column};
7508    
7509 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7510 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7511 wakaba 1.54 }
7512    
7513 wakaba 1.56 $self->{insertion_mode} = IN_CELL_IM;
7514 wakaba 1.54
7515     push @$active_formatting_elements, ['#marker', ''];
7516    
7517 wakaba 1.122
7518 wakaba 1.54 $token = $self->_get_next_token;
7519 wakaba 1.123 next B;
7520 wakaba 1.54 } elsif ({
7521     caption => 1, col => 1, colgroup => 1,
7522     tbody => 1, tfoot => 1, thead => 1,
7523 wakaba 1.56 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
7524 wakaba 1.54 }->{$token->{tag_name}}) {
7525 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
7526 wakaba 1.54 ## As if </tr>
7527     ## have an element in table scope
7528     my $i;
7529     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7530     my $node = $self->{open_elements}->[$_];
7531 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
7532 wakaba 1.79
7533 wakaba 1.54 $i = $_;
7534     last INSCOPE;
7535 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7536 wakaba 1.79
7537 wakaba 1.54 last INSCOPE;
7538     }
7539     } # INSCOPE
7540 wakaba 1.79 unless (defined $i) {
7541 wakaba 1.122
7542 wakaba 1.83 ## TODO: This type is wrong.
7543 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmacthed end tag',
7544     text => $token->{tag_name}, token => $token);
7545 wakaba 1.54 ## Ignore the token
7546 wakaba 1.122
7547 wakaba 1.54 $token = $self->_get_next_token;
7548 wakaba 1.123 next B;
7549 wakaba 1.54 }
7550    
7551     ## Clear back to table row context
7552 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7553     & TABLE_ROW_SCOPING_EL)) {
7554 wakaba 1.79
7555 wakaba 1.83 ## ISSUE: Can this case be reached?
7556 wakaba 1.54 pop @{$self->{open_elements}};
7557     }
7558    
7559     pop @{$self->{open_elements}}; # tr
7560 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7561 wakaba 1.54 if ($token->{tag_name} eq 'tr') {
7562 wakaba 1.79
7563 wakaba 1.54 ## reprocess
7564 wakaba 1.122
7565 wakaba 1.123 next B;
7566 wakaba 1.54 } else {
7567 wakaba 1.79
7568 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
7569     }
7570     }
7571 wakaba 1.52
7572 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
7573 wakaba 1.54 ## have an element in table scope
7574     my $i;
7575     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7576     my $node = $self->{open_elements}->[$_];
7577 wakaba 1.121 if ($node->[1] & TABLE_ROW_GROUP_EL) {
7578 wakaba 1.79
7579 wakaba 1.54 $i = $_;
7580     last INSCOPE;
7581 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7582 wakaba 1.79
7583 wakaba 1.54 last INSCOPE;
7584     }
7585     } # INSCOPE
7586     unless (defined $i) {
7587 wakaba 1.79
7588 wakaba 1.150 ## TODO: This erorr type is wrong.
7589     $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7590     text => $token->{tag_name}, token => $token);
7591 wakaba 1.54 ## Ignore the token
7592 wakaba 1.122
7593 wakaba 1.54 $token = $self->_get_next_token;
7594 wakaba 1.123 next B;
7595 wakaba 1.54 }
7596 wakaba 1.52
7597 wakaba 1.54 ## Clear back to table body context
7598 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7599     & TABLE_ROWS_SCOPING_EL)) {
7600 wakaba 1.79
7601 wakaba 1.83 ## ISSUE: Can this state be reached?
7602 wakaba 1.54 pop @{$self->{open_elements}};
7603     }
7604    
7605     ## As if <{current node}>
7606     ## have an element in table scope
7607     ## true by definition
7608    
7609     ## Clear back to table body context
7610     ## nop by definition
7611    
7612     pop @{$self->{open_elements}};
7613 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7614 wakaba 1.54 ## reprocess in "in table" insertion mode...
7615 wakaba 1.79 } else {
7616    
7617 wakaba 1.54 }
7618 wakaba 1.51
7619 wakaba 1.54 if ($token->{tag_name} eq 'col') {
7620     ## Clear back to table context
7621 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7622     & TABLE_SCOPING_EL)) {
7623 wakaba 1.79
7624 wakaba 1.83 ## ISSUE: Can this state be reached?
7625 wakaba 1.54 pop @{$self->{open_elements}};
7626     }
7627    
7628    
7629 wakaba 1.51 {
7630     my $el;
7631    
7632     $el = $self->{document}->create_element_ns
7633 wakaba 1.128 ($HTML_NS, [undef, 'colgroup']);
7634 wakaba 1.51
7635 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7636     if defined $token->{line};
7637     $el->set_user_data (manakai_source_column => $token->{column})
7638     if defined $token->{column};
7639    
7640 wakaba 1.51 $self->{open_elements}->[-1]->[0]->append_child ($el);
7641 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'colgroup'} || 0];
7642 wakaba 1.51 }
7643    
7644 wakaba 1.56 $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
7645 wakaba 1.54 ## reprocess
7646 wakaba 1.122
7647 wakaba 1.123 next B;
7648 wakaba 1.54 } elsif ({
7649     caption => 1,
7650     colgroup => 1,
7651     tbody => 1, tfoot => 1, thead => 1,
7652     }->{$token->{tag_name}}) {
7653     ## Clear back to table context
7654 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7655     & TABLE_SCOPING_EL)) {
7656 wakaba 1.79
7657 wakaba 1.83 ## ISSUE: Can this state be reached?
7658 wakaba 1.54 pop @{$self->{open_elements}};
7659     }
7660    
7661     push @$active_formatting_elements, ['#marker', '']
7662     if $token->{tag_name} eq 'caption';
7663    
7664 wakaba 1.52
7665 wakaba 1.54 {
7666     my $el;
7667    
7668     $el = $self->{document}->create_element_ns
7669 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7670 wakaba 1.52
7671 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
7672 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7673 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7674 wakaba 1.117 $attr->value ($attr_t->{value});
7675     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7676     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7677     $el->set_attribute_node_ns ($attr);
7678 wakaba 1.52 }
7679    
7680 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7681     if defined $token->{line};
7682     $el->set_user_data (manakai_source_column => $token->{column})
7683     if defined $token->{column};
7684    
7685 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7686 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7687 wakaba 1.54 }
7688    
7689     $self->{insertion_mode} = {
7690 wakaba 1.56 caption => IN_CAPTION_IM,
7691     colgroup => IN_COLUMN_GROUP_IM,
7692     tbody => IN_TABLE_BODY_IM,
7693     tfoot => IN_TABLE_BODY_IM,
7694     thead => IN_TABLE_BODY_IM,
7695 wakaba 1.54 }->{$token->{tag_name}};
7696 wakaba 1.52 $token = $self->_get_next_token;
7697 wakaba 1.122
7698 wakaba 1.123 next B;
7699 wakaba 1.54 } else {
7700     die "$0: in table: <>: $token->{tag_name}";
7701     }
7702     } elsif ($token->{tag_name} eq 'table') {
7703 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7704     text => $self->{open_elements}->[-1]->[0]
7705 wakaba 1.120 ->manakai_local_name,
7706     token => $token);
7707 wakaba 1.54
7708     ## As if </table>
7709     ## have a table element in table scope
7710     my $i;
7711     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7712     my $node = $self->{open_elements}->[$_];
7713 wakaba 1.121 if ($node->[1] & TABLE_EL) {
7714 wakaba 1.79
7715 wakaba 1.54 $i = $_;
7716     last INSCOPE;
7717 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7718 wakaba 1.79
7719 wakaba 1.54 last INSCOPE;
7720     }
7721     } # INSCOPE
7722     unless (defined $i) {
7723 wakaba 1.79
7724 wakaba 1.83 ## TODO: The following is wrong, maybe.
7725 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => 'table',
7726     token => $token);
7727 wakaba 1.54 ## Ignore tokens </table><table>
7728 wakaba 1.122
7729 wakaba 1.52 $token = $self->_get_next_token;
7730 wakaba 1.123 next B;
7731 wakaba 1.52 }
7732    
7733 wakaba 1.149 ## TODO: Followings are removed from the latest spec.
7734 wakaba 1.54 ## generate implied end tags
7735 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7736 wakaba 1.79
7737 wakaba 1.86 pop @{$self->{open_elements}};
7738 wakaba 1.54 }
7739    
7740 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
7741 wakaba 1.79
7742 wakaba 1.120 ## NOTE: |<table><tr><table>|
7743 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7744     text => $self->{open_elements}->[-1]->[0]
7745 wakaba 1.120 ->manakai_local_name,
7746     token => $token);
7747 wakaba 1.79 } else {
7748    
7749 wakaba 1.54 }
7750    
7751     splice @{$self->{open_elements}}, $i;
7752 wakaba 1.95 pop @{$open_tables};
7753 wakaba 1.54
7754     $self->_reset_insertion_mode;
7755 wakaba 1.52
7756 wakaba 1.122 ## reprocess
7757    
7758 wakaba 1.123 next B;
7759 wakaba 1.100 } elsif ($token->{tag_name} eq 'style') {
7760     if (not $open_tables->[-1]->[1]) { # tainted
7761    
7762     ## NOTE: This is a "as if in head" code clone.
7763     $parse_rcdata->(CDATA_CONTENT_MODEL);
7764 wakaba 1.123 next B;
7765 wakaba 1.100 } else {
7766    
7767     #
7768     }
7769     } elsif ($token->{tag_name} eq 'script') {
7770     if (not $open_tables->[-1]->[1]) { # tainted
7771    
7772     ## NOTE: This is a "as if in head" code clone.
7773     $script_start_tag->();
7774 wakaba 1.123 next B;
7775 wakaba 1.100 } else {
7776    
7777     #
7778     }
7779 wakaba 1.98 } elsif ($token->{tag_name} eq 'input') {
7780     if (not $open_tables->[-1]->[1]) { # tainted
7781     if ($token->{attributes}->{type}) { ## TODO: case
7782     my $type = lc $token->{attributes}->{type}->{value};
7783     if ($type eq 'hidden') {
7784    
7785 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table',
7786     text => $token->{tag_name}, token => $token);
7787 wakaba 1.98
7788    
7789     {
7790     my $el;
7791    
7792     $el = $self->{document}->create_element_ns
7793 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7794 wakaba 1.98
7795     for my $attr_name (keys %{ $token->{attributes}}) {
7796 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7797 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7798 wakaba 1.117 $attr->value ($attr_t->{value});
7799     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7800     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7801     $el->set_attribute_node_ns ($attr);
7802 wakaba 1.98 }
7803    
7804 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7805     if defined $token->{line};
7806     $el->set_user_data (manakai_source_column => $token->{column})
7807     if defined $token->{column};
7808    
7809 wakaba 1.98 $self->{open_elements}->[-1]->[0]->append_child ($el);
7810 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7811 wakaba 1.98 }
7812    
7813    
7814     ## TODO: form element pointer
7815    
7816     pop @{$self->{open_elements}};
7817    
7818     $token = $self->_get_next_token;
7819 wakaba 1.122 delete $self->{self_closing};
7820 wakaba 1.123 next B;
7821 wakaba 1.98 } else {
7822    
7823     #
7824     }
7825     } else {
7826    
7827     #
7828     }
7829     } else {
7830    
7831     #
7832     }
7833 wakaba 1.60 } else {
7834 wakaba 1.79
7835 wakaba 1.60 #
7836     }
7837 wakaba 1.98
7838 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table', text => $token->{tag_name},
7839     token => $token);
7840 wakaba 1.98
7841     $insert = $insert_to_foster;
7842     #
7843 wakaba 1.60 } elsif ($token->{type} == END_TAG_TOKEN) {
7844 wakaba 1.54 if ($token->{tag_name} eq 'tr' and
7845 wakaba 1.56 $self->{insertion_mode} == IN_ROW_IM) {
7846 wakaba 1.54 ## have an element in table scope
7847     my $i;
7848     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7849     my $node = $self->{open_elements}->[$_];
7850 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
7851 wakaba 1.79
7852 wakaba 1.54 $i = $_;
7853     last INSCOPE;
7854 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7855 wakaba 1.79
7856 wakaba 1.54 last INSCOPE;
7857     }
7858     } # INSCOPE
7859     unless (defined $i) {
7860 wakaba 1.79
7861 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7862     text => $token->{tag_name}, token => $token);
7863 wakaba 1.54 ## Ignore the token
7864 wakaba 1.122
7865 wakaba 1.54 $token = $self->_get_next_token;
7866 wakaba 1.123 next B;
7867 wakaba 1.79 } else {
7868    
7869 wakaba 1.54 }
7870    
7871     ## Clear back to table row context
7872 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7873     & TABLE_ROW_SCOPING_EL)) {
7874 wakaba 1.79
7875 wakaba 1.83 ## ISSUE: Can this state be reached?
7876 wakaba 1.54 pop @{$self->{open_elements}};
7877     }
7878    
7879     pop @{$self->{open_elements}}; # tr
7880 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7881 wakaba 1.54 $token = $self->_get_next_token;
7882 wakaba 1.122
7883 wakaba 1.123 next B;
7884 wakaba 1.54 } elsif ($token->{tag_name} eq 'table') {
7885 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
7886 wakaba 1.54 ## As if </tr>
7887     ## have an element in table scope
7888     my $i;
7889     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7890     my $node = $self->{open_elements}->[$_];
7891 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
7892 wakaba 1.79
7893 wakaba 1.54 $i = $_;
7894     last INSCOPE;
7895 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7896 wakaba 1.79
7897 wakaba 1.54 last INSCOPE;
7898     }
7899     } # INSCOPE
7900     unless (defined $i) {
7901 wakaba 1.79
7902 wakaba 1.83 ## TODO: The following is wrong.
7903 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7904     text => $token->{type}, token => $token);
7905 wakaba 1.54 ## Ignore the token
7906 wakaba 1.122
7907 wakaba 1.54 $token = $self->_get_next_token;
7908 wakaba 1.123 next B;
7909 wakaba 1.54 }
7910    
7911     ## Clear back to table row context
7912 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7913     & TABLE_ROW_SCOPING_EL)) {
7914 wakaba 1.79
7915 wakaba 1.83 ## ISSUE: Can this state be reached?
7916 wakaba 1.54 pop @{$self->{open_elements}};
7917     }
7918    
7919     pop @{$self->{open_elements}}; # tr
7920 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7921 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
7922     }
7923    
7924 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
7925 wakaba 1.54 ## have an element in table scope
7926     my $i;
7927     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7928     my $node = $self->{open_elements}->[$_];
7929 wakaba 1.121 if ($node->[1] & TABLE_ROW_GROUP_EL) {
7930 wakaba 1.79
7931 wakaba 1.54 $i = $_;
7932     last INSCOPE;
7933 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7934 wakaba 1.79
7935 wakaba 1.54 last INSCOPE;
7936     }
7937     } # INSCOPE
7938     unless (defined $i) {
7939 wakaba 1.79
7940 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7941     text => $token->{tag_name}, token => $token);
7942 wakaba 1.54 ## Ignore the token
7943 wakaba 1.122
7944 wakaba 1.54 $token = $self->_get_next_token;
7945 wakaba 1.123 next B;
7946 wakaba 1.54 }
7947    
7948     ## Clear back to table body context
7949 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7950     & TABLE_ROWS_SCOPING_EL)) {
7951 wakaba 1.79
7952 wakaba 1.54 pop @{$self->{open_elements}};
7953     }
7954    
7955     ## As if <{current node}>
7956     ## have an element in table scope
7957     ## true by definition
7958    
7959     ## Clear back to table body context
7960     ## nop by definition
7961    
7962     pop @{$self->{open_elements}};
7963 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7964 wakaba 1.54 ## reprocess in the "in table" insertion mode...
7965     }
7966 wakaba 1.52
7967 wakaba 1.94 ## NOTE: </table> in the "in table" insertion mode.
7968     ## When you edit the code fragment below, please ensure that
7969     ## the code for <table> in the "in table" insertion mode
7970     ## is synced with it.
7971    
7972 wakaba 1.54 ## have a table element in table scope
7973     my $i;
7974     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7975     my $node = $self->{open_elements}->[$_];
7976 wakaba 1.121 if ($node->[1] & TABLE_EL) {
7977 wakaba 1.79
7978 wakaba 1.54 $i = $_;
7979     last INSCOPE;
7980 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7981 wakaba 1.79
7982 wakaba 1.54 last INSCOPE;
7983     }
7984     } # INSCOPE
7985     unless (defined $i) {
7986 wakaba 1.79
7987 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7988     text => $token->{tag_name}, token => $token);
7989 wakaba 1.54 ## Ignore the token
7990 wakaba 1.122
7991 wakaba 1.54 $token = $self->_get_next_token;
7992 wakaba 1.123 next B;
7993 wakaba 1.51 }
7994 wakaba 1.54
7995     splice @{$self->{open_elements}}, $i;
7996 wakaba 1.95 pop @{$open_tables};
7997 wakaba 1.54
7998     $self->_reset_insertion_mode;
7999 wakaba 1.1
8000     $token = $self->_get_next_token;
8001 wakaba 1.123 next B;
8002 wakaba 1.54 } elsif ({
8003     tbody => 1, tfoot => 1, thead => 1,
8004     }->{$token->{tag_name}} and
8005 wakaba 1.58 $self->{insertion_mode} & ROW_IMS) {
8006 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
8007 wakaba 1.54 ## have an element in table scope
8008     my $i;
8009     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8010     my $node = $self->{open_elements}->[$_];
8011 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8012 wakaba 1.79
8013 wakaba 1.54 $i = $_;
8014     last INSCOPE;
8015 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8016 wakaba 1.79
8017 wakaba 1.54 last INSCOPE;
8018     }
8019     } # INSCOPE
8020     unless (defined $i) {
8021 wakaba 1.79
8022 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8023     text => $token->{tag_name}, token => $token);
8024 wakaba 1.54 ## Ignore the token
8025 wakaba 1.122
8026 wakaba 1.54 $token = $self->_get_next_token;
8027 wakaba 1.123 next B;
8028 wakaba 1.54 }
8029    
8030     ## As if </tr>
8031     ## have an element in table scope
8032     my $i;
8033     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8034     my $node = $self->{open_elements}->[$_];
8035 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
8036 wakaba 1.79
8037 wakaba 1.54 $i = $_;
8038     last INSCOPE;
8039 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8040 wakaba 1.79
8041 wakaba 1.54 last INSCOPE;
8042     }
8043     } # INSCOPE
8044     unless (defined $i) {
8045 wakaba 1.79
8046 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8047     text => 'tr', token => $token);
8048 wakaba 1.54 ## Ignore the token
8049 wakaba 1.122
8050 wakaba 1.54 $token = $self->_get_next_token;
8051 wakaba 1.123 next B;
8052 wakaba 1.54 }
8053    
8054     ## Clear back to table row context
8055 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8056     & TABLE_ROW_SCOPING_EL)) {
8057 wakaba 1.79
8058 wakaba 1.83 ## ISSUE: Can this case be reached?
8059 wakaba 1.54 pop @{$self->{open_elements}};
8060     }
8061    
8062     pop @{$self->{open_elements}}; # tr
8063 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
8064 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
8065 wakaba 1.34 }
8066    
8067 wakaba 1.54 ## have an element in table scope
8068     my $i;
8069     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8070     my $node = $self->{open_elements}->[$_];
8071 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8072 wakaba 1.79
8073 wakaba 1.54 $i = $_;
8074     last INSCOPE;
8075 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8076 wakaba 1.79
8077 wakaba 1.54 last INSCOPE;
8078 wakaba 1.34 }
8079 wakaba 1.54 } # INSCOPE
8080     unless (defined $i) {
8081 wakaba 1.79
8082 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8083     text => $token->{tag_name}, token => $token);
8084 wakaba 1.54 ## Ignore the token
8085 wakaba 1.122
8086 wakaba 1.54 $token = $self->_get_next_token;
8087 wakaba 1.123 next B;
8088 wakaba 1.34 }
8089    
8090 wakaba 1.54 ## Clear back to table body context
8091 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8092     & TABLE_ROWS_SCOPING_EL)) {
8093 wakaba 1.79
8094 wakaba 1.83 ## ISSUE: Can this case be reached?
8095 wakaba 1.51 pop @{$self->{open_elements}};
8096 wakaba 1.25 }
8097 wakaba 1.51
8098 wakaba 1.54 pop @{$self->{open_elements}};
8099 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8100 wakaba 1.122
8101 wakaba 1.54 $token = $self->_get_next_token;
8102 wakaba 1.123 next B;
8103 wakaba 1.54 } elsif ({
8104     body => 1, caption => 1, col => 1, colgroup => 1,
8105     html => 1, td => 1, th => 1,
8106 wakaba 1.56 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
8107     tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
8108 wakaba 1.54 }->{$token->{tag_name}}) {
8109 wakaba 1.122
8110 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8111     text => $token->{tag_name}, token => $token);
8112 wakaba 1.122 ## Ignore the token
8113    
8114     $token = $self->_get_next_token;
8115 wakaba 1.123 next B;
8116 wakaba 1.60 } else {
8117 wakaba 1.79
8118 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table:/',
8119     text => $token->{tag_name}, token => $token);
8120 wakaba 1.54
8121 wakaba 1.60 $insert = $insert_to_foster;
8122     #
8123     }
8124 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8125 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
8126 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8127 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
8128 wakaba 1.103
8129 wakaba 1.104 #
8130 wakaba 1.103 } else {
8131    
8132 wakaba 1.104 #
8133 wakaba 1.103 }
8134    
8135     ## Stop parsing
8136     last B;
8137 wakaba 1.60 } else {
8138     die "$0: $token->{type}: Unknown token type";
8139     }
8140 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
8141 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8142 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
8143     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
8144     unless (length $token->{data}) {
8145 wakaba 1.79
8146 wakaba 1.54 $token = $self->_get_next_token;
8147 wakaba 1.123 next B;
8148 wakaba 1.25 }
8149 wakaba 1.54 }
8150    
8151 wakaba 1.79
8152 wakaba 1.54 #
8153 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
8154 wakaba 1.54 if ($token->{tag_name} eq 'col') {
8155    
8156 wakaba 1.79
8157 wakaba 1.25 {
8158     my $el;
8159    
8160 wakaba 1.1 $el = $self->{document}->create_element_ns
8161 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8162 wakaba 1.1
8163 wakaba 1.25 for my $attr_name (keys %{ $token->{attributes}}) {
8164 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8165 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8166 wakaba 1.117 $attr->value ($attr_t->{value});
8167     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8168     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8169     $el->set_attribute_node_ns ($attr);
8170 wakaba 1.1 }
8171    
8172 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8173     if defined $token->{line};
8174     $el->set_user_data (manakai_source_column => $token->{column})
8175     if defined $token->{column};
8176    
8177 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($el);
8178 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8179 wakaba 1.25 }
8180    
8181 wakaba 1.54 pop @{$self->{open_elements}};
8182 wakaba 1.122 delete $self->{self_closing};
8183 wakaba 1.54 $token = $self->_get_next_token;
8184 wakaba 1.123 next B;
8185 wakaba 1.54 } else {
8186 wakaba 1.79
8187 wakaba 1.54 #
8188     }
8189 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
8190 wakaba 1.54 if ($token->{tag_name} eq 'colgroup') {
8191 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
8192 wakaba 1.79
8193 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8194     text => 'colgroup', token => $token);
8195 wakaba 1.25 ## Ignore the token
8196 wakaba 1.42 $token = $self->_get_next_token;
8197 wakaba 1.123 next B;
8198 wakaba 1.24 } else {
8199 wakaba 1.79
8200 wakaba 1.54 pop @{$self->{open_elements}}; # colgroup
8201 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8202 wakaba 1.54 $token = $self->_get_next_token;
8203 wakaba 1.123 next B;
8204 wakaba 1.25 }
8205 wakaba 1.54 } elsif ($token->{tag_name} eq 'col') {
8206 wakaba 1.79
8207 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8208     text => 'col', token => $token);
8209 wakaba 1.54 ## Ignore the token
8210     $token = $self->_get_next_token;
8211 wakaba 1.123 next B;
8212 wakaba 1.54 } else {
8213 wakaba 1.79
8214 wakaba 1.54 #
8215     }
8216 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8217 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
8218 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8219    
8220     ## Stop parsing.
8221     last B;
8222     } else {
8223     ## NOTE: As if </colgroup>.
8224    
8225     pop @{$self->{open_elements}}; # colgroup
8226     $self->{insertion_mode} = IN_TABLE_IM;
8227     ## Reprocess.
8228 wakaba 1.123 next B;
8229 wakaba 1.103 }
8230     } else {
8231     die "$0: $token->{type}: Unknown token type";
8232     }
8233 wakaba 1.51
8234 wakaba 1.54 ## As if </colgroup>
8235 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
8236 wakaba 1.79
8237 wakaba 1.103 ## TODO: Wrong error type?
8238 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8239     text => 'colgroup', token => $token);
8240 wakaba 1.54 ## Ignore the token
8241 wakaba 1.122
8242 wakaba 1.54 $token = $self->_get_next_token;
8243 wakaba 1.123 next B;
8244 wakaba 1.54 } else {
8245 wakaba 1.79
8246 wakaba 1.54 pop @{$self->{open_elements}}; # colgroup
8247 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8248 wakaba 1.122
8249 wakaba 1.54 ## reprocess
8250 wakaba 1.123 next B;
8251 wakaba 1.54 }
8252 wakaba 1.101 } elsif ($self->{insertion_mode} & SELECT_IMS) {
8253 wakaba 1.60 if ($token->{type} == CHARACTER_TOKEN) {
8254 wakaba 1.79
8255 wakaba 1.60 $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
8256     $token = $self->_get_next_token;
8257 wakaba 1.123 next B;
8258 wakaba 1.60 } elsif ($token->{type} == START_TAG_TOKEN) {
8259 wakaba 1.121 if ($token->{tag_name} eq 'option') {
8260     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8261    
8262     ## As if </option>
8263     pop @{$self->{open_elements}};
8264     } else {
8265    
8266     }
8267 wakaba 1.51
8268 wakaba 1.121
8269 wakaba 1.1 {
8270     my $el;
8271    
8272     $el = $self->{document}->create_element_ns
8273 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8274 wakaba 1.1
8275     for my $attr_name (keys %{ $token->{attributes}}) {
8276 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8277 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8278 wakaba 1.117 $attr->value ($attr_t->{value});
8279     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8280     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8281     $el->set_attribute_node_ns ($attr);
8282 wakaba 1.1 }
8283    
8284 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8285     if defined $token->{line};
8286     $el->set_user_data (manakai_source_column => $token->{column})
8287     if defined $token->{column};
8288    
8289 wakaba 1.3 $self->{open_elements}->[-1]->[0]->append_child ($el);
8290 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8291 wakaba 1.1 }
8292    
8293 wakaba 1.122
8294 wakaba 1.121 $token = $self->_get_next_token;
8295 wakaba 1.123 next B;
8296 wakaba 1.121 } elsif ($token->{tag_name} eq 'optgroup') {
8297     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8298    
8299     ## As if </option>
8300     pop @{$self->{open_elements}};
8301     } else {
8302    
8303     }
8304 wakaba 1.54
8305 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
8306    
8307     ## As if </optgroup>
8308     pop @{$self->{open_elements}};
8309     } else {
8310    
8311     }
8312 wakaba 1.51
8313 wakaba 1.121
8314 wakaba 1.1 {
8315     my $el;
8316    
8317     $el = $self->{document}->create_element_ns
8318 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8319 wakaba 1.1
8320 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
8321 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8322 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8323 wakaba 1.117 $attr->value ($attr_t->{value});
8324     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8325     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8326     $el->set_attribute_node_ns ($attr);
8327 wakaba 1.54 }
8328    
8329 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8330     if defined $token->{line};
8331     $el->set_user_data (manakai_source_column => $token->{column})
8332     if defined $token->{column};
8333    
8334 wakaba 1.3 $self->{open_elements}->[-1]->[0]->append_child ($el);
8335 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8336 wakaba 1.1 }
8337    
8338 wakaba 1.122
8339 wakaba 1.121 $token = $self->_get_next_token;
8340 wakaba 1.123 next B;
8341 wakaba 1.143 } elsif ({
8342     select => 1, input => 1, textarea => 1,
8343     }->{$token->{tag_name}} or
8344 wakaba 1.101 ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
8345     {
8346     caption => 1, table => 1,
8347     tbody => 1, tfoot => 1, thead => 1,
8348     tr => 1, td => 1, th => 1,
8349     }->{$token->{tag_name}})) {
8350     ## TODO: The type below is not good - <select> is replaced by </select>
8351 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'select',
8352     token => $token);
8353 wakaba 1.101 ## NOTE: As if the token were </select> (<select> case) or
8354     ## as if there were </select> (otherwise).
8355 wakaba 1.121 ## have an element in table scope
8356     my $i;
8357     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8358     my $node = $self->{open_elements}->[$_];
8359     if ($node->[1] & SELECT_EL) {
8360    
8361     $i = $_;
8362     last INSCOPE;
8363     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8364 wakaba 1.54
8365 wakaba 1.121 last INSCOPE;
8366     }
8367     } # INSCOPE
8368     unless (defined $i) {
8369    
8370 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8371     text => 'select', token => $token);
8372 wakaba 1.121 ## Ignore the token
8373 wakaba 1.122
8374 wakaba 1.121 $token = $self->_get_next_token;
8375 wakaba 1.123 next B;
8376 wakaba 1.121 }
8377 wakaba 1.79
8378 wakaba 1.121
8379     splice @{$self->{open_elements}}, $i;
8380 wakaba 1.54
8381 wakaba 1.121 $self->_reset_insertion_mode;
8382 wakaba 1.54
8383 wakaba 1.101 if ($token->{tag_name} eq 'select') {
8384    
8385     $token = $self->_get_next_token;
8386 wakaba 1.123 next B;
8387 wakaba 1.101 } else {
8388    
8389 wakaba 1.122
8390 wakaba 1.101 ## Reprocess the token.
8391 wakaba 1.123 next B;
8392 wakaba 1.101 }
8393 wakaba 1.60 } else {
8394 wakaba 1.79
8395 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in select',
8396     text => $token->{tag_name}, token => $token);
8397 wakaba 1.60 ## Ignore the token
8398 wakaba 1.122
8399 wakaba 1.60 $token = $self->_get_next_token;
8400 wakaba 1.123 next B;
8401 wakaba 1.60 }
8402     } elsif ($token->{type} == END_TAG_TOKEN) {
8403 wakaba 1.121 if ($token->{tag_name} eq 'optgroup') {
8404     if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
8405     $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
8406    
8407     ## As if </option>
8408     splice @{$self->{open_elements}}, -2;
8409     } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
8410    
8411     pop @{$self->{open_elements}};
8412     } else {
8413    
8414 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8415     text => $token->{tag_name}, token => $token);
8416 wakaba 1.121 ## Ignore the token
8417     }
8418 wakaba 1.122
8419 wakaba 1.121 $token = $self->_get_next_token;
8420 wakaba 1.123 next B;
8421 wakaba 1.121 } elsif ($token->{tag_name} eq 'option') {
8422     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8423    
8424     pop @{$self->{open_elements}};
8425     } else {
8426    
8427 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8428     text => $token->{tag_name}, token => $token);
8429 wakaba 1.121 ## Ignore the token
8430     }
8431 wakaba 1.122
8432 wakaba 1.121 $token = $self->_get_next_token;
8433 wakaba 1.123 next B;
8434 wakaba 1.121 } elsif ($token->{tag_name} eq 'select') {
8435     ## have an element in table scope
8436     my $i;
8437     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8438     my $node = $self->{open_elements}->[$_];
8439     if ($node->[1] & SELECT_EL) {
8440    
8441     $i = $_;
8442     last INSCOPE;
8443     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8444 wakaba 1.54
8445 wakaba 1.121 last INSCOPE;
8446     }
8447     } # INSCOPE
8448     unless (defined $i) {
8449    
8450 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8451     text => $token->{tag_name}, token => $token);
8452 wakaba 1.121 ## Ignore the token
8453 wakaba 1.122
8454 wakaba 1.121 $token = $self->_get_next_token;
8455 wakaba 1.123 next B;
8456 wakaba 1.121 }
8457 wakaba 1.79
8458 wakaba 1.121
8459     splice @{$self->{open_elements}}, $i;
8460 wakaba 1.54
8461 wakaba 1.121 $self->_reset_insertion_mode;
8462 wakaba 1.54
8463 wakaba 1.122
8464 wakaba 1.121 $token = $self->_get_next_token;
8465 wakaba 1.123 next B;
8466 wakaba 1.101 } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
8467     {
8468     caption => 1, table => 1, tbody => 1,
8469     tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
8470     }->{$token->{tag_name}}) {
8471 wakaba 1.83 ## TODO: The following is wrong?
8472 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8473     text => $token->{tag_name}, token => $token);
8474 wakaba 1.121
8475     ## have an element in table scope
8476     my $i;
8477     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8478     my $node = $self->{open_elements}->[$_];
8479     if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8480    
8481     $i = $_;
8482     last INSCOPE;
8483     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8484    
8485     last INSCOPE;
8486     }
8487     } # INSCOPE
8488     unless (defined $i) {
8489    
8490     ## Ignore the token
8491 wakaba 1.122
8492 wakaba 1.121 $token = $self->_get_next_token;
8493 wakaba 1.123 next B;
8494 wakaba 1.121 }
8495 wakaba 1.54
8496 wakaba 1.121 ## As if </select>
8497     ## have an element in table scope
8498     undef $i;
8499     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8500     my $node = $self->{open_elements}->[$_];
8501     if ($node->[1] & SELECT_EL) {
8502 wakaba 1.54
8503 wakaba 1.121 $i = $_;
8504     last INSCOPE;
8505     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8506 wakaba 1.83 ## ISSUE: Can this state be reached?
8507 wakaba 1.121
8508     last INSCOPE;
8509     }
8510     } # INSCOPE
8511     unless (defined $i) {
8512    
8513 wakaba 1.83 ## TODO: The following error type is correct?
8514 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8515     text => 'select', token => $token);
8516 wakaba 1.121 ## Ignore the </select> token
8517 wakaba 1.122
8518 wakaba 1.121 $token = $self->_get_next_token; ## TODO: ok?
8519 wakaba 1.123 next B;
8520 wakaba 1.121 }
8521 wakaba 1.54
8522 wakaba 1.121
8523     splice @{$self->{open_elements}}, $i;
8524 wakaba 1.54
8525 wakaba 1.121 $self->_reset_insertion_mode;
8526 wakaba 1.54
8527 wakaba 1.122
8528 wakaba 1.121 ## reprocess
8529 wakaba 1.123 next B;
8530 wakaba 1.60 } else {
8531 wakaba 1.79
8532 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in select:/',
8533     text => $token->{tag_name}, token => $token);
8534 wakaba 1.54 ## Ignore the token
8535 wakaba 1.122
8536 wakaba 1.54 $token = $self->_get_next_token;
8537 wakaba 1.123 next B;
8538 wakaba 1.60 }
8539 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8540 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
8541 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8542    
8543 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
8544 wakaba 1.103 } else {
8545    
8546     }
8547    
8548     ## Stop parsing.
8549     last B;
8550 wakaba 1.60 } else {
8551     die "$0: $token->{type}: Unknown token type";
8552     }
8553 wakaba 1.58 } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
8554 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8555 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
8556     my $data = $1;
8557     ## As if in body
8558     $reconstruct_active_formatting_elements->($insert_to_current);
8559    
8560     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
8561    
8562     unless (length $token->{data}) {
8563 wakaba 1.79
8564 wakaba 1.54 $token = $self->_get_next_token;
8565 wakaba 1.123 next B;
8566 wakaba 1.54 }
8567     }
8568    
8569 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
8570 wakaba 1.79
8571 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:#text', token => $token);
8572 wakaba 1.54
8573 wakaba 1.85 ## Reprocess in the "after body" insertion mode.
8574 wakaba 1.79 } else {
8575    
8576 wakaba 1.54 }
8577    
8578     ## "after body" insertion mode
8579 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body:#text', token => $token);
8580 wakaba 1.54
8581 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
8582 wakaba 1.54 ## reprocess
8583 wakaba 1.123 next B;
8584 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
8585 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
8586 wakaba 1.79
8587 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html',
8588     text => $token->{tag_name}, token => $token);
8589 wakaba 1.54
8590 wakaba 1.85 ## Reprocess in the "after body" insertion mode.
8591 wakaba 1.79 } else {
8592    
8593 wakaba 1.54 }
8594    
8595     ## "after body" insertion mode
8596 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body',
8597     text => $token->{tag_name}, token => $token);
8598 wakaba 1.54
8599 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
8600 wakaba 1.122
8601 wakaba 1.54 ## reprocess
8602 wakaba 1.123 next B;
8603 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
8604 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
8605 wakaba 1.79
8606 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:/',
8607     text => $token->{tag_name}, token => $token);
8608 wakaba 1.54
8609 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
8610 wakaba 1.85 ## Reprocess in the "after body" insertion mode.
8611 wakaba 1.79 } else {
8612    
8613 wakaba 1.54 }
8614    
8615     ## "after body" insertion mode
8616     if ($token->{tag_name} eq 'html') {
8617     if (defined $self->{inner_html_node}) {
8618 wakaba 1.79
8619 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8620     text => 'html', token => $token);
8621 wakaba 1.54 ## Ignore the token
8622     $token = $self->_get_next_token;
8623 wakaba 1.123 next B;
8624 wakaba 1.54 } else {
8625 wakaba 1.79
8626 wakaba 1.56 $self->{insertion_mode} = AFTER_HTML_BODY_IM;
8627 wakaba 1.54 $token = $self->_get_next_token;
8628 wakaba 1.123 next B;
8629 wakaba 1.54 }
8630     } else {
8631 wakaba 1.79
8632 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body:/',
8633     text => $token->{tag_name}, token => $token);
8634 wakaba 1.54
8635 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
8636 wakaba 1.54 ## reprocess
8637 wakaba 1.123 next B;
8638 wakaba 1.54 }
8639 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8640    
8641     ## Stop parsing
8642     last B;
8643 wakaba 1.54 } else {
8644     die "$0: $token->{type}: Unknown token type";
8645     }
8646 wakaba 1.58 } elsif ($self->{insertion_mode} & FRAME_IMS) {
8647 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8648 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
8649     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
8650    
8651     unless (length $token->{data}) {
8652 wakaba 1.79
8653 wakaba 1.54 $token = $self->_get_next_token;
8654 wakaba 1.123 next B;
8655 wakaba 1.54 }
8656     }
8657    
8658     if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
8659 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
8660 wakaba 1.79
8661 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset:#text', token => $token);
8662 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
8663 wakaba 1.79
8664 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset:#text', token => $token);
8665 wakaba 1.154 } else { # "after after frameset"
8666 wakaba 1.79
8667 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:#text', token => $token);
8668 wakaba 1.54 }
8669    
8670     ## Ignore the token.
8671     if (length $token->{data}) {
8672 wakaba 1.79
8673 wakaba 1.54 ## reprocess the rest of characters
8674     } else {
8675 wakaba 1.79
8676 wakaba 1.54 $token = $self->_get_next_token;
8677     }
8678 wakaba 1.123 next B;
8679 wakaba 1.54 }
8680    
8681     die qq[$0: Character "$token->{data}"];
8682 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
8683 wakaba 1.54 if ($token->{tag_name} eq 'frameset' and
8684 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
8685 wakaba 1.54
8686 wakaba 1.79
8687 wakaba 1.54 {
8688     my $el;
8689    
8690     $el = $self->{document}->create_element_ns
8691 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8692 wakaba 1.54
8693     for my $attr_name (keys %{ $token->{attributes}}) {
8694 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8695 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8696 wakaba 1.117 $attr->value ($attr_t->{value});
8697     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8698     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8699     $el->set_attribute_node_ns ($attr);
8700 wakaba 1.54 }
8701    
8702 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8703     if defined $token->{line};
8704     $el->set_user_data (manakai_source_column => $token->{column})
8705     if defined $token->{column};
8706    
8707 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
8708 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8709 wakaba 1.54 }
8710    
8711 wakaba 1.122
8712 wakaba 1.54 $token = $self->_get_next_token;
8713 wakaba 1.123 next B;
8714 wakaba 1.54 } elsif ($token->{tag_name} eq 'frame' and
8715 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
8716 wakaba 1.54
8717 wakaba 1.79
8718 wakaba 1.54 {
8719     my $el;
8720    
8721     $el = $self->{document}->create_element_ns
8722 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8723 wakaba 1.54
8724     for my $attr_name (keys %{ $token->{attributes}}) {
8725 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8726 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8727 wakaba 1.117 $attr->value ($attr_t->{value});
8728     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8729     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8730     $el->set_attribute_node_ns ($attr);
8731 wakaba 1.54 }
8732    
8733 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8734     if defined $token->{line};
8735     $el->set_user_data (manakai_source_column => $token->{column})
8736     if defined $token->{column};
8737    
8738 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
8739 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8740 wakaba 1.54 }
8741    
8742     pop @{$self->{open_elements}};
8743 wakaba 1.122 delete $self->{self_closing};
8744 wakaba 1.54 $token = $self->_get_next_token;
8745 wakaba 1.123 next B;
8746 wakaba 1.54 } elsif ($token->{tag_name} eq 'noframes') {
8747 wakaba 1.79
8748 wakaba 1.145 ## NOTE: As if in head.
8749 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
8750 wakaba 1.123 next B;
8751 wakaba 1.155
8752     ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
8753     ## has no parse error.
8754 wakaba 1.54 } else {
8755 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
8756 wakaba 1.79
8757 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset',
8758     text => $token->{tag_name}, token => $token);
8759 wakaba 1.154 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
8760 wakaba 1.79
8761 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset',
8762     text => $token->{tag_name}, token => $token);
8763 wakaba 1.154 } else { # "after after frameset"
8764    
8765     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after after frameset',
8766     text => $token->{tag_name}, token => $token);
8767 wakaba 1.54 }
8768     ## Ignore the token
8769 wakaba 1.122
8770 wakaba 1.54 $token = $self->_get_next_token;
8771 wakaba 1.123 next B;
8772 wakaba 1.54 }
8773 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
8774 wakaba 1.54 if ($token->{tag_name} eq 'frameset' and
8775 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
8776 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
8777 wakaba 1.54 @{$self->{open_elements}} == 1) {
8778 wakaba 1.79
8779 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8780     text => $token->{tag_name}, token => $token);
8781 wakaba 1.54 ## Ignore the token
8782     $token = $self->_get_next_token;
8783     } else {
8784 wakaba 1.79
8785 wakaba 1.54 pop @{$self->{open_elements}};
8786     $token = $self->_get_next_token;
8787     }
8788 wakaba 1.43
8789 wakaba 1.54 if (not defined $self->{inner_html_node} and
8790 wakaba 1.121 not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
8791 wakaba 1.79
8792 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
8793 wakaba 1.79 } else {
8794    
8795 wakaba 1.54 }
8796 wakaba 1.123 next B;
8797 wakaba 1.54 } elsif ($token->{tag_name} eq 'html' and
8798 wakaba 1.56 $self->{insertion_mode} == AFTER_FRAMESET_IM) {
8799 wakaba 1.79
8800 wakaba 1.56 $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
8801 wakaba 1.54 $token = $self->_get_next_token;
8802 wakaba 1.123 next B;
8803 wakaba 1.54 } else {
8804 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
8805 wakaba 1.79
8806 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset:/',
8807     text => $token->{tag_name}, token => $token);
8808 wakaba 1.154 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
8809 wakaba 1.79
8810 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset:/',
8811     text => $token->{tag_name}, token => $token);
8812 wakaba 1.154 } else { # "after after html"
8813    
8814     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after after frameset:/',
8815     text => $token->{tag_name}, token => $token);
8816 wakaba 1.54 }
8817     ## Ignore the token
8818     $token = $self->_get_next_token;
8819 wakaba 1.123 next B;
8820 wakaba 1.54 }
8821 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8822 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
8823 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8824    
8825 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
8826 wakaba 1.103 } else {
8827    
8828     }
8829    
8830     ## Stop parsing
8831     last B;
8832 wakaba 1.54 } else {
8833     die "$0: $token->{type}: Unknown token type";
8834     }
8835 wakaba 1.43
8836 wakaba 1.54 ## ISSUE: An issue in spec here
8837     } else {
8838     die "$0: $self->{insertion_mode}: Unknown insertion mode";
8839     }
8840 wakaba 1.43
8841 wakaba 1.54 ## "in body" insertion mode
8842 wakaba 1.57 if ($token->{type} == START_TAG_TOKEN) {
8843 wakaba 1.54 if ($token->{tag_name} eq 'script') {
8844 wakaba 1.79
8845 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
8846 wakaba 1.100 $script_start_tag->();
8847 wakaba 1.123 next B;
8848 wakaba 1.54 } elsif ($token->{tag_name} eq 'style') {
8849 wakaba 1.79
8850 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
8851 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
8852 wakaba 1.123 next B;
8853 wakaba 1.54 } elsif ({
8854     base => 1, link => 1,
8855     }->{$token->{tag_name}}) {
8856 wakaba 1.79
8857 wakaba 1.54 ## NOTE: This is an "as if in head" code clone, only "-t" differs
8858    
8859     {
8860     my $el;
8861    
8862     $el = $self->{document}->create_element_ns
8863 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8864 wakaba 1.54
8865     for my $attr_name (keys %{ $token->{attributes}}) {
8866 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8867 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8868 wakaba 1.117 $attr->value ($attr_t->{value});
8869     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8870     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8871     $el->set_attribute_node_ns ($attr);
8872 wakaba 1.54 }
8873    
8874 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8875     if defined $token->{line};
8876     $el->set_user_data (manakai_source_column => $token->{column})
8877     if defined $token->{column};
8878    
8879 wakaba 1.54 $insert->($el);
8880 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8881 wakaba 1.54 }
8882    
8883     pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
8884 wakaba 1.122 delete $self->{self_closing};
8885 wakaba 1.54 $token = $self->_get_next_token;
8886 wakaba 1.123 next B;
8887 wakaba 1.54 } elsif ($token->{tag_name} eq 'meta') {
8888     ## NOTE: This is an "as if in head" code clone, only "-t" differs
8889    
8890     {
8891     my $el;
8892    
8893     $el = $self->{document}->create_element_ns
8894 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8895 wakaba 1.54
8896     for my $attr_name (keys %{ $token->{attributes}}) {
8897 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8898 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8899 wakaba 1.117 $attr->value ($attr_t->{value});
8900     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8901     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8902     $el->set_attribute_node_ns ($attr);
8903 wakaba 1.54 }
8904    
8905 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8906     if defined $token->{line};
8907     $el->set_user_data (manakai_source_column => $token->{column})
8908     if defined $token->{column};
8909    
8910 wakaba 1.54 $insert->($el);
8911 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8912 wakaba 1.54 }
8913    
8914 wakaba 1.68 my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
8915 wakaba 1.43
8916 wakaba 1.54 unless ($self->{confident}) {
8917 wakaba 1.131 if ($token->{attributes}->{charset}) {
8918 wakaba 1.79
8919 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
8920     ## in the {change_encoding} callback.
8921 wakaba 1.65 $self->{change_encoding}
8922 wakaba 1.112 ->($self, $token->{attributes}->{charset}->{value}, $token);
8923 wakaba 1.68
8924     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
8925     ->set_user_data (manakai_has_reference =>
8926     $token->{attributes}->{charset}
8927     ->{has_reference});
8928 wakaba 1.65 } elsif ($token->{attributes}->{content}) {
8929     if ($token->{attributes}->{content}->{value}
8930 wakaba 1.141 =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
8931 wakaba 1.71 [\x09-\x0D\x20]*=
8932 wakaba 1.54 [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
8933 wakaba 1.142 ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {
8934 wakaba 1.79
8935 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
8936     ## in the {change_encoding} callback.
8937 wakaba 1.65 $self->{change_encoding}
8938 wakaba 1.112 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
8939 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
8940     ->set_user_data (manakai_has_reference =>
8941     $token->{attributes}->{content}
8942     ->{has_reference});
8943 wakaba 1.65 }
8944 wakaba 1.54 }
8945 wakaba 1.68 } else {
8946     if ($token->{attributes}->{charset}) {
8947 wakaba 1.79
8948 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
8949     ->set_user_data (manakai_has_reference =>
8950     $token->{attributes}->{charset}
8951     ->{has_reference});
8952     }
8953 wakaba 1.69 if ($token->{attributes}->{content}) {
8954 wakaba 1.79
8955 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
8956     ->set_user_data (manakai_has_reference =>
8957     $token->{attributes}->{content}
8958     ->{has_reference});
8959     }
8960 wakaba 1.54 }
8961 wakaba 1.43
8962 wakaba 1.122 delete $self->{self_closing};
8963 wakaba 1.54 $token = $self->_get_next_token;
8964 wakaba 1.123 next B;
8965 wakaba 1.54 } elsif ($token->{tag_name} eq 'title') {
8966 wakaba 1.79
8967 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
8968 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
8969 wakaba 1.123 next B;
8970 wakaba 1.54 } elsif ($token->{tag_name} eq 'body') {
8971 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body', text => 'body', token => $token);
8972 wakaba 1.1
8973 wakaba 1.54 if (@{$self->{open_elements}} == 1 or
8974 wakaba 1.121 not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
8975 wakaba 1.79
8976 wakaba 1.54 ## Ignore the token
8977     } else {
8978     my $body_el = $self->{open_elements}->[1]->[0];
8979     for my $attr_name (keys %{$token->{attributes}}) {
8980     unless ($body_el->has_attribute_ns (undef, $attr_name)) {
8981 wakaba 1.79
8982 wakaba 1.54 $body_el->set_attribute_ns
8983     (undef, [undef, $attr_name],
8984     $token->{attributes}->{$attr_name}->{value});
8985 wakaba 1.1 }
8986 wakaba 1.54 }
8987     }
8988 wakaba 1.122
8989 wakaba 1.54 $token = $self->_get_next_token;
8990 wakaba 1.123 next B;
8991 wakaba 1.54 } elsif ({
8992     address => 1, blockquote => 1, center => 1, dir => 1,
8993 wakaba 1.85 div => 1, dl => 1, fieldset => 1,
8994     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
8995 wakaba 1.97 menu => 1, ol => 1, p => 1, ul => 1,
8996     pre => 1, listing => 1,
8997 wakaba 1.107 form => 1,
8998     table => 1,
8999     hr => 1,
9000 wakaba 1.54 }->{$token->{tag_name}}) {
9001 wakaba 1.107 if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
9002    
9003 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in form:form', token => $token);
9004 wakaba 1.107 ## Ignore the token
9005 wakaba 1.122
9006 wakaba 1.107 $token = $self->_get_next_token;
9007 wakaba 1.123 next B;
9008 wakaba 1.107 }
9009    
9010 wakaba 1.54 ## has a p element in scope
9011     INSCOPE: for (reverse @{$self->{open_elements}}) {
9012 wakaba 1.121 if ($_->[1] & P_EL) {
9013 wakaba 1.79
9014 wakaba 1.122
9015     $token->{self_closing} = $self->{self_closing};
9016     unshift @{$self->{token}}, $token;
9017     delete $self->{self_closing};
9018     # <form>
9019 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9020     line => $token->{line}, column => $token->{column}};
9021 wakaba 1.123 next B;
9022 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9023 wakaba 1.79
9024 wakaba 1.54 last INSCOPE;
9025     }
9026     } # INSCOPE
9027    
9028    
9029 wakaba 1.48 {
9030     my $el;
9031    
9032     $el = $self->{document}->create_element_ns
9033 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9034 wakaba 1.48
9035 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
9036 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9037 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9038 wakaba 1.117 $attr->value ($attr_t->{value});
9039     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9040     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9041     $el->set_attribute_node_ns ($attr);
9042 wakaba 1.54 }
9043    
9044 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9045     if defined $token->{line};
9046     $el->set_user_data (manakai_source_column => $token->{column})
9047     if defined $token->{column};
9048    
9049 wakaba 1.54 $insert->($el);
9050 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9051 wakaba 1.48 }
9052    
9053 wakaba 1.97 if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
9054 wakaba 1.122
9055 wakaba 1.54 $token = $self->_get_next_token;
9056 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
9057 wakaba 1.54 $token->{data} =~ s/^\x0A//;
9058     unless (length $token->{data}) {
9059 wakaba 1.79
9060 wakaba 1.54 $token = $self->_get_next_token;
9061 wakaba 1.79 } else {
9062    
9063 wakaba 1.54 }
9064 wakaba 1.79 } else {
9065    
9066 wakaba 1.54 }
9067 wakaba 1.107 } elsif ($token->{tag_name} eq 'form') {
9068    
9069     $self->{form_element} = $self->{open_elements}->[-1]->[0];
9070    
9071 wakaba 1.122
9072 wakaba 1.107 $token = $self->_get_next_token;
9073     } elsif ($token->{tag_name} eq 'table') {
9074    
9075     push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
9076 wakaba 1.79
9077 wakaba 1.107 $self->{insertion_mode} = IN_TABLE_IM;
9078    
9079 wakaba 1.122
9080 wakaba 1.54 $token = $self->_get_next_token;
9081 wakaba 1.107 } elsif ($token->{tag_name} eq 'hr') {
9082 wakaba 1.79
9083 wakaba 1.107 pop @{$self->{open_elements}};
9084    
9085 wakaba 1.122
9086 wakaba 1.54 $token = $self->_get_next_token;
9087     } else {
9088    
9089     $token = $self->_get_next_token;
9090     }
9091 wakaba 1.123 next B;
9092 wakaba 1.107 } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
9093 wakaba 1.54 ## has a p element in scope
9094     INSCOPE: for (reverse @{$self->{open_elements}}) {
9095 wakaba 1.121 if ($_->[1] & P_EL) {
9096 wakaba 1.79
9097 wakaba 1.122
9098     $token->{self_closing} = $self->{self_closing};
9099     unshift @{$self->{token}}, $token;
9100     delete $self->{self_closing};
9101     # <x>
9102 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9103     line => $token->{line}, column => $token->{column}};
9104 wakaba 1.123 next B;
9105 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9106 wakaba 1.79
9107 wakaba 1.54 last INSCOPE;
9108     }
9109     } # INSCOPE
9110    
9111     ## Step 1
9112     my $i = -1;
9113     my $node = $self->{open_elements}->[$i];
9114 wakaba 1.107 my $li_or_dtdd = {li => {li => 1},
9115     dt => {dt => 1, dd => 1},
9116     dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
9117 wakaba 1.54 LI: {
9118     ## Step 2
9119 wakaba 1.121 if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
9120 wakaba 1.54 if ($i != -1) {
9121 wakaba 1.79
9122 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9123     text => $self->{open_elements}->[-1]->[0]
9124 wakaba 1.120 ->manakai_local_name,
9125     token => $token);
9126 wakaba 1.79 } else {
9127    
9128 wakaba 1.54 }
9129     splice @{$self->{open_elements}}, $i;
9130     last LI;
9131 wakaba 1.79 } else {
9132    
9133 wakaba 1.54 }
9134    
9135     ## Step 3
9136 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
9137 wakaba 1.54 #not $phrasing_category->{$node->[1]} and
9138 wakaba 1.121 ($node->[1] & SPECIAL_EL or
9139     $node->[1] & SCOPING_EL) and
9140     not ($node->[1] & ADDRESS_EL) and
9141     not ($node->[1] & DIV_EL)) {
9142 wakaba 1.79
9143 wakaba 1.54 last LI;
9144     }
9145    
9146 wakaba 1.79
9147 wakaba 1.54 ## Step 4
9148     $i--;
9149     $node = $self->{open_elements}->[$i];
9150     redo LI;
9151     } # LI
9152    
9153    
9154 wakaba 1.49 {
9155     my $el;
9156    
9157     $el = $self->{document}->create_element_ns
9158 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9159 wakaba 1.49
9160     for my $attr_name (keys %{ $token->{attributes}}) {
9161 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9162 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9163 wakaba 1.117 $attr->value ($attr_t->{value});
9164     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9165     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9166     $el->set_attribute_node_ns ($attr);
9167 wakaba 1.49 }
9168    
9169 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9170     if defined $token->{line};
9171     $el->set_user_data (manakai_source_column => $token->{column})
9172     if defined $token->{column};
9173    
9174 wakaba 1.54 $insert->($el);
9175 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9176 wakaba 1.49 }
9177    
9178 wakaba 1.122
9179 wakaba 1.54 $token = $self->_get_next_token;
9180 wakaba 1.123 next B;
9181 wakaba 1.54 } elsif ($token->{tag_name} eq 'plaintext') {
9182     ## has a p element in scope
9183     INSCOPE: for (reverse @{$self->{open_elements}}) {
9184 wakaba 1.121 if ($_->[1] & P_EL) {
9185 wakaba 1.79
9186 wakaba 1.122
9187     $token->{self_closing} = $self->{self_closing};
9188     unshift @{$self->{token}}, $token;
9189     delete $self->{self_closing};
9190     # <plaintext>
9191 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9192     line => $token->{line}, column => $token->{column}};
9193 wakaba 1.123 next B;
9194 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9195 wakaba 1.79
9196 wakaba 1.54 last INSCOPE;
9197     }
9198     } # INSCOPE
9199    
9200    
9201 wakaba 1.1 {
9202     my $el;
9203    
9204     $el = $self->{document}->create_element_ns
9205 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9206 wakaba 1.1
9207 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
9208 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9209 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9210 wakaba 1.117 $attr->value ($attr_t->{value});
9211     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9212     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9213     $el->set_attribute_node_ns ($attr);
9214 wakaba 1.54 }
9215    
9216 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9217     if defined $token->{line};
9218     $el->set_user_data (manakai_source_column => $token->{column})
9219     if defined $token->{column};
9220    
9221 wakaba 1.54 $insert->($el);
9222 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9223 wakaba 1.1 }
9224    
9225 wakaba 1.54
9226     $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
9227    
9228 wakaba 1.122
9229 wakaba 1.54 $token = $self->_get_next_token;
9230 wakaba 1.123 next B;
9231 wakaba 1.54 } elsif ($token->{tag_name} eq 'a') {
9232     AFE: for my $i (reverse 0..$#$active_formatting_elements) {
9233     my $node = $active_formatting_elements->[$i];
9234 wakaba 1.121 if ($node->[1] & A_EL) {
9235 wakaba 1.79
9236 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in a:a', token => $token);
9237 wakaba 1.54
9238 wakaba 1.122
9239     $token->{self_closing} = $self->{self_closing};
9240     unshift @{$self->{token}}, $token;
9241     delete $self->{self_closing};
9242     # <a>
9243 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'a',
9244     line => $token->{line}, column => $token->{column}};
9245 wakaba 1.111 $formatting_end_tag->($token);
9246 wakaba 1.54
9247     AFE2: for (reverse 0..$#$active_formatting_elements) {
9248     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
9249 wakaba 1.79
9250 wakaba 1.54 splice @$active_formatting_elements, $_, 1;
9251     last AFE2;
9252 wakaba 1.49 }
9253 wakaba 1.54 } # AFE2
9254     OE: for (reverse 0..$#{$self->{open_elements}}) {
9255     if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
9256 wakaba 1.79
9257 wakaba 1.54 splice @{$self->{open_elements}}, $_, 1;
9258     last OE;
9259 wakaba 1.49 }
9260 wakaba 1.54 } # OE
9261     last AFE;
9262     } elsif ($node->[0] eq '#marker') {
9263 wakaba 1.79
9264 wakaba 1.54 last AFE;
9265     }
9266     } # AFE
9267    
9268     $reconstruct_active_formatting_elements->($insert_to_current);
9269 wakaba 1.49
9270 wakaba 1.54
9271     {
9272     my $el;
9273    
9274     $el = $self->{document}->create_element_ns
9275 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9276 wakaba 1.54
9277     for my $attr_name (keys %{ $token->{attributes}}) {
9278 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9279 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9280 wakaba 1.117 $attr->value ($attr_t->{value});
9281     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9282     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9283     $el->set_attribute_node_ns ($attr);
9284 wakaba 1.54 }
9285    
9286 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9287     if defined $token->{line};
9288     $el->set_user_data (manakai_source_column => $token->{column})
9289     if defined $token->{column};
9290    
9291 wakaba 1.54 $insert->($el);
9292 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9293 wakaba 1.54 }
9294    
9295     push @$active_formatting_elements, $self->{open_elements}->[-1];
9296 wakaba 1.48
9297 wakaba 1.122
9298 wakaba 1.54 $token = $self->_get_next_token;
9299 wakaba 1.123 next B;
9300 wakaba 1.54 } elsif ($token->{tag_name} eq 'nobr') {
9301     $reconstruct_active_formatting_elements->($insert_to_current);
9302 wakaba 1.1
9303 wakaba 1.54 ## has a |nobr| element in scope
9304     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9305     my $node = $self->{open_elements}->[$_];
9306 wakaba 1.121 if ($node->[1] & NOBR_EL) {
9307 wakaba 1.79
9308 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in nobr:nobr', token => $token);
9309 wakaba 1.122
9310     $token->{self_closing} = $self->{self_closing};
9311     unshift @{$self->{token}}, $token;
9312     delete $self->{self_closing};
9313     # <nobr>
9314 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
9315     line => $token->{line}, column => $token->{column}};
9316 wakaba 1.123 next B;
9317 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9318 wakaba 1.79
9319 wakaba 1.54 last INSCOPE;
9320     }
9321     } # INSCOPE
9322    
9323    
9324     {
9325     my $el;
9326    
9327     $el = $self->{document}->create_element_ns
9328 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9329 wakaba 1.54
9330     for my $attr_name (keys %{ $token->{attributes}}) {
9331 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9332 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9333 wakaba 1.117 $attr->value ($attr_t->{value});
9334     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9335     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9336     $el->set_attribute_node_ns ($attr);
9337 wakaba 1.54 }
9338    
9339 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9340     if defined $token->{line};
9341     $el->set_user_data (manakai_source_column => $token->{column})
9342     if defined $token->{column};
9343    
9344 wakaba 1.54 $insert->($el);
9345 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9346 wakaba 1.54 }
9347    
9348     push @$active_formatting_elements, $self->{open_elements}->[-1];
9349    
9350 wakaba 1.122
9351 wakaba 1.54 $token = $self->_get_next_token;
9352 wakaba 1.123 next B;
9353 wakaba 1.54 } elsif ($token->{tag_name} eq 'button') {
9354     ## has a button element in scope
9355     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9356     my $node = $self->{open_elements}->[$_];
9357 wakaba 1.121 if ($node->[1] & BUTTON_EL) {
9358 wakaba 1.79
9359 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in button:button', token => $token);
9360 wakaba 1.122
9361     $token->{self_closing} = $self->{self_closing};
9362     unshift @{$self->{token}}, $token;
9363     delete $self->{self_closing};
9364     # <button>
9365 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'button',
9366     line => $token->{line}, column => $token->{column}};
9367 wakaba 1.123 next B;
9368 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9369 wakaba 1.79
9370 wakaba 1.54 last INSCOPE;
9371 wakaba 1.1 }
9372 wakaba 1.54 } # INSCOPE
9373    
9374     $reconstruct_active_formatting_elements->($insert_to_current);
9375    
9376    
9377     {
9378     my $el;
9379    
9380     $el = $self->{document}->create_element_ns
9381 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9382 wakaba 1.54
9383     for my $attr_name (keys %{ $token->{attributes}}) {
9384 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9385 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9386 wakaba 1.117 $attr->value ($attr_t->{value});
9387     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9388     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9389     $el->set_attribute_node_ns ($attr);
9390 wakaba 1.54 }
9391    
9392 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9393     if defined $token->{line};
9394     $el->set_user_data (manakai_source_column => $token->{column})
9395     if defined $token->{column};
9396    
9397 wakaba 1.54 $insert->($el);
9398 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9399 wakaba 1.54 }
9400    
9401 wakaba 1.86
9402     ## TODO: associate with $self->{form_element} if defined
9403    
9404 wakaba 1.54 push @$active_formatting_elements, ['#marker', ''];
9405 wakaba 1.1
9406 wakaba 1.122
9407 wakaba 1.54 $token = $self->_get_next_token;
9408 wakaba 1.123 next B;
9409 wakaba 1.102 } elsif ({
9410 wakaba 1.107 xmp => 1,
9411     iframe => 1,
9412     noembed => 1,
9413 wakaba 1.145 noframes => 1, ## NOTE: This is an "as if in head" code clone.
9414 wakaba 1.107 noscript => 0, ## TODO: 1 if scripting is enabled
9415 wakaba 1.102 }->{$token->{tag_name}}) {
9416 wakaba 1.107 if ($token->{tag_name} eq 'xmp') {
9417    
9418     $reconstruct_active_formatting_elements->($insert_to_current);
9419     } else {
9420    
9421 wakaba 1.1 }
9422 wakaba 1.107 ## NOTE: There is an "as if in body" code clone.
9423 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
9424 wakaba 1.123 next B;
9425 wakaba 1.54 } elsif ($token->{tag_name} eq 'isindex') {
9426 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'isindex', token => $token);
9427 wakaba 1.54
9428     if (defined $self->{form_element}) {
9429 wakaba 1.79
9430 wakaba 1.54 ## Ignore the token
9431 wakaba 1.122 ## NOTE: Not acknowledged.
9432 wakaba 1.54 $token = $self->_get_next_token;
9433 wakaba 1.123 next B;
9434 wakaba 1.54 } else {
9435 wakaba 1.144 delete $self->{self_closing};
9436    
9437 wakaba 1.54 my $at = $token->{attributes};
9438     my $form_attrs;
9439     $form_attrs->{action} = $at->{action} if $at->{action};
9440     my $prompt_attr = $at->{prompt};
9441     $at->{name} = {name => 'name', value => 'isindex'};
9442     delete $at->{action};
9443     delete $at->{prompt};
9444     my @tokens = (
9445 wakaba 1.57 {type => START_TAG_TOKEN, tag_name => 'form',
9446 wakaba 1.112 attributes => $form_attrs,
9447     line => $token->{line}, column => $token->{column}},
9448     {type => START_TAG_TOKEN, tag_name => 'hr',
9449     line => $token->{line}, column => $token->{column}},
9450     {type => START_TAG_TOKEN, tag_name => 'p',
9451     line => $token->{line}, column => $token->{column}},
9452     {type => START_TAG_TOKEN, tag_name => 'label',
9453     line => $token->{line}, column => $token->{column}},
9454 wakaba 1.54 );
9455     if ($prompt_attr) {
9456 wakaba 1.79
9457 wakaba 1.112 push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
9458 wakaba 1.116 #line => $token->{line}, column => $token->{column},
9459     };
9460 wakaba 1.1 } else {
9461 wakaba 1.79
9462 wakaba 1.57 push @tokens, {type => CHARACTER_TOKEN,
9463 wakaba 1.112 data => 'This is a searchable index. Insert your search keywords here: ',
9464 wakaba 1.116 #line => $token->{line}, column => $token->{column},
9465     }; # SHOULD
9466 wakaba 1.54 ## TODO: make this configurable
9467 wakaba 1.1 }
9468 wakaba 1.54 push @tokens,
9469 wakaba 1.112 {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
9470     line => $token->{line}, column => $token->{column}},
9471 wakaba 1.57 #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
9472 wakaba 1.112 {type => END_TAG_TOKEN, tag_name => 'label',
9473     line => $token->{line}, column => $token->{column}},
9474     {type => END_TAG_TOKEN, tag_name => 'p',
9475     line => $token->{line}, column => $token->{column}},
9476     {type => START_TAG_TOKEN, tag_name => 'hr',
9477     line => $token->{line}, column => $token->{column}},
9478     {type => END_TAG_TOKEN, tag_name => 'form',
9479     line => $token->{line}, column => $token->{column}};
9480 wakaba 1.54 unshift @{$self->{token}}, (@tokens);
9481 wakaba 1.122 $token = $self->_get_next_token;
9482 wakaba 1.123 next B;
9483 wakaba 1.54 }
9484     } elsif ($token->{tag_name} eq 'textarea') {
9485     my $tag_name = $token->{tag_name};
9486     my $el;
9487    
9488     $el = $self->{document}->create_element_ns
9489 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9490 wakaba 1.54
9491     for my $attr_name (keys %{ $token->{attributes}}) {
9492 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9493 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9494 wakaba 1.117 $attr->value ($attr_t->{value});
9495     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9496     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9497     $el->set_attribute_node_ns ($attr);
9498 wakaba 1.54 }
9499    
9500 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9501     if defined $token->{line};
9502     $el->set_user_data (manakai_source_column => $token->{column})
9503     if defined $token->{column};
9504    
9505 wakaba 1.54
9506     ## TODO: $self->{form_element} if defined
9507     $self->{content_model} = RCDATA_CONTENT_MODEL;
9508     delete $self->{escape}; # MUST
9509    
9510     $insert->($el);
9511    
9512     my $text = '';
9513 wakaba 1.122
9514 wakaba 1.54 $token = $self->_get_next_token;
9515 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
9516 wakaba 1.54 $token->{data} =~ s/^\x0A//;
9517 wakaba 1.53 unless (length $token->{data}) {
9518 wakaba 1.79
9519 wakaba 1.53 $token = $self->_get_next_token;
9520 wakaba 1.79 } else {
9521    
9522 wakaba 1.53 }
9523 wakaba 1.79 } else {
9524    
9525 wakaba 1.53 }
9526 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) {
9527 wakaba 1.79
9528 wakaba 1.54 $text .= $token->{data};
9529     $token = $self->_get_next_token;
9530     }
9531     if (length $text) {
9532 wakaba 1.79
9533 wakaba 1.54 $el->manakai_append_text ($text);
9534     }
9535    
9536     $self->{content_model} = PCDATA_CONTENT_MODEL;
9537    
9538 wakaba 1.57 if ($token->{type} == END_TAG_TOKEN and
9539 wakaba 1.54 $token->{tag_name} eq $tag_name) {
9540 wakaba 1.79
9541 wakaba 1.54 ## Ignore the token
9542     } else {
9543 wakaba 1.79
9544 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in RCDATA:#eof', token => $token);
9545 wakaba 1.54 }
9546     $token = $self->_get_next_token;
9547 wakaba 1.123 next B;
9548 wakaba 1.149 } elsif ($token->{tag_name} eq 'rt' or
9549     $token->{tag_name} eq 'rp') {
9550     ## has a |ruby| element in scope
9551     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9552     my $node = $self->{open_elements}->[$_];
9553     if ($node->[1] & RUBY_EL) {
9554    
9555     ## generate implied end tags
9556     while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
9557    
9558     pop @{$self->{open_elements}};
9559     }
9560     unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
9561    
9562 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9563     text => $self->{open_elements}->[-1]->[0]
9564 wakaba 1.149 ->manakai_local_name,
9565     token => $token);
9566     pop @{$self->{open_elements}}
9567     while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
9568     }
9569     last INSCOPE;
9570     } elsif ($node->[1] & SCOPING_EL) {
9571    
9572     last INSCOPE;
9573     }
9574     } # INSCOPE
9575    
9576    
9577     {
9578     my $el;
9579    
9580     $el = $self->{document}->create_element_ns
9581     ($HTML_NS, [undef, $token->{tag_name}]);
9582    
9583     for my $attr_name (keys %{ $token->{attributes}}) {
9584     my $attr_t = $token->{attributes}->{$attr_name};
9585     my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9586     $attr->value ($attr_t->{value});
9587     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9588     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9589     $el->set_attribute_node_ns ($attr);
9590     }
9591    
9592     $el->set_user_data (manakai_source_line => $token->{line})
9593     if defined $token->{line};
9594     $el->set_user_data (manakai_source_column => $token->{column})
9595     if defined $token->{column};
9596    
9597     $insert->($el);
9598     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9599     }
9600    
9601    
9602    
9603     $token = $self->_get_next_token;
9604     redo B;
9605 wakaba 1.123 } elsif ($token->{tag_name} eq 'math' or
9606     $token->{tag_name} eq 'svg') {
9607     $reconstruct_active_formatting_elements->($insert_to_current);
9608 wakaba 1.128
9609 wakaba 1.152 ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
9610    
9611 wakaba 1.128 ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
9612    
9613     ## "adjust foreign attributes" - done in insert-element-f
9614 wakaba 1.123
9615    
9616     {
9617     my $el;
9618    
9619     $el = $self->{document}->create_element_ns
9620 wakaba 1.128 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, [undef, $token->{tag_name}]);
9621 wakaba 1.123
9622     for my $attr_name (keys %{ $token->{attributes}}) {
9623     my $attr_t = $token->{attributes}->{$attr_name};
9624 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (
9625     @{
9626     $foreign_attr_xname->{$attr_name} ||
9627     [undef, [undef,
9628 wakaba 1.152 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS) eq $SVG_NS ?
9629 wakaba 1.128 ($svg_attr_name->{$attr_name} || $attr_name) :
9630 wakaba 1.152 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS) eq $MML_NS ?
9631     ($attr_name eq 'definitionurl' ?
9632     'definitionURL' : $attr_name) :
9633 wakaba 1.128 $attr_name]]
9634     }
9635     );
9636 wakaba 1.123 $attr->value ($attr_t->{value});
9637     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9638     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9639     $el->set_attribute_node_ns ($attr);
9640     }
9641    
9642     $el->set_user_data (manakai_source_line => $token->{line})
9643     if defined $token->{line};
9644     $el->set_user_data (manakai_source_column => $token->{column})
9645     if defined $token->{column};
9646    
9647     $insert->($el);
9648     push @{$self->{open_elements}}, [$el, ($el_category_f->{$token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS}->{ $token->{tag_name}} || 0) | FOREIGN_EL];
9649 wakaba 1.128
9650     if ( $token->{attributes}->{xmlns} and $token->{attributes}->{xmlns}->{value} ne ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS)) {
9651 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad namespace', token => $token);
9652 wakaba 1.128 ## TODO: Error type documentation
9653     }
9654 wakaba 1.123 }
9655    
9656    
9657     if ($self->{self_closing}) {
9658     pop @{$self->{open_elements}};
9659     delete $self->{self_closing};
9660     } else {
9661    
9662     $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
9663     ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
9664     ## mode, "in body" (not "in foreign content") secondary insertion
9665     ## mode, maybe.
9666     }
9667    
9668     $token = $self->_get_next_token;
9669     next B;
9670 wakaba 1.54 } elsif ({
9671     caption => 1, col => 1, colgroup => 1, frame => 1,
9672     frameset => 1, head => 1, option => 1, optgroup => 1,
9673     tbody => 1, td => 1, tfoot => 1, th => 1,
9674     thead => 1, tr => 1,
9675     }->{$token->{tag_name}}) {
9676 wakaba 1.79
9677 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body',
9678     text => $token->{tag_name}, token => $token);
9679 wakaba 1.54 ## Ignore the token
9680 wakaba 1.122 ## NOTE: |<col/>| or |<frame/>| here is an error.
9681 wakaba 1.54 $token = $self->_get_next_token;
9682 wakaba 1.123 next B;
9683 wakaba 1.54
9684     ## ISSUE: An issue on HTML5 new elements in the spec.
9685     } else {
9686 wakaba 1.108 if ($token->{tag_name} eq 'image') {
9687    
9688 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'image', token => $token);
9689 wakaba 1.108 $token->{tag_name} = 'img';
9690     } else {
9691    
9692     }
9693    
9694     ## NOTE: There is an "as if <br>" code clone.
9695 wakaba 1.54 $reconstruct_active_formatting_elements->($insert_to_current);
9696 wakaba 1.53
9697 wakaba 1.54
9698     {
9699     my $el;
9700    
9701     $el = $self->{document}->create_element_ns
9702 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9703 wakaba 1.54
9704     for my $attr_name (keys %{ $token->{attributes}}) {
9705 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9706 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9707 wakaba 1.117 $attr->value ($attr_t->{value});
9708     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9709     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9710     $el->set_attribute_node_ns ($attr);
9711 wakaba 1.53 }
9712 wakaba 1.54
9713 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9714     if defined $token->{line};
9715     $el->set_user_data (manakai_source_column => $token->{column})
9716     if defined $token->{column};
9717    
9718 wakaba 1.54 $insert->($el);
9719 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9720 wakaba 1.54 }
9721    
9722 wakaba 1.107
9723 wakaba 1.108 if ({
9724     applet => 1, marquee => 1, object => 1,
9725     }->{$token->{tag_name}}) {
9726    
9727     push @$active_formatting_elements, ['#marker', ''];
9728 wakaba 1.122
9729 wakaba 1.108 } elsif ({
9730     b => 1, big => 1, em => 1, font => 1, i => 1,
9731     s => 1, small => 1, strile => 1,
9732     strong => 1, tt => 1, u => 1,
9733     }->{$token->{tag_name}}) {
9734    
9735     push @$active_formatting_elements, $self->{open_elements}->[-1];
9736 wakaba 1.122
9737 wakaba 1.108 } elsif ($token->{tag_name} eq 'input') {
9738    
9739     ## TODO: associate with $self->{form_element} if defined
9740     pop @{$self->{open_elements}};
9741 wakaba 1.122 delete $self->{self_closing};
9742 wakaba 1.108 } elsif ({
9743     area => 1, basefont => 1, bgsound => 1, br => 1,
9744     embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
9745     #image => 1,
9746     }->{$token->{tag_name}}) {
9747    
9748     pop @{$self->{open_elements}};
9749 wakaba 1.122 delete $self->{self_closing};
9750 wakaba 1.108 } elsif ($token->{tag_name} eq 'select') {
9751 wakaba 1.107 ## TODO: associate with $self->{form_element} if defined
9752    
9753     if ($self->{insertion_mode} & TABLE_IMS or
9754     $self->{insertion_mode} & BODY_TABLE_IMS or
9755     $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
9756    
9757     $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
9758     } else {
9759    
9760     $self->{insertion_mode} = IN_SELECT_IM;
9761     }
9762 wakaba 1.122
9763 wakaba 1.108 } else {
9764    
9765 wakaba 1.107 }
9766 wakaba 1.53
9767 wakaba 1.54 $token = $self->_get_next_token;
9768 wakaba 1.123 next B;
9769 wakaba 1.54 }
9770 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
9771 wakaba 1.54 if ($token->{tag_name} eq 'body') {
9772 wakaba 1.105 ## has a |body| element in scope
9773     my $i;
9774 wakaba 1.109 INSCOPE: {
9775     for (reverse @{$self->{open_elements}}) {
9776 wakaba 1.121 if ($_->[1] & BODY_EL) {
9777 wakaba 1.109
9778     $i = $_;
9779     last INSCOPE;
9780 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9781 wakaba 1.109
9782     last;
9783     }
9784 wakaba 1.54 }
9785 wakaba 1.109
9786 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'start tag not allowed',
9787     text => $token->{tag_name}, token => $token);
9788 wakaba 1.105 ## NOTE: Ignore the token.
9789 wakaba 1.54 $token = $self->_get_next_token;
9790 wakaba 1.123 next B;
9791 wakaba 1.109 } # INSCOPE
9792 wakaba 1.105
9793     for (@{$self->{open_elements}}) {
9794 wakaba 1.121 unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
9795 wakaba 1.105
9796 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9797     text => $_->[0]->manakai_local_name,
9798 wakaba 1.120 token => $token);
9799 wakaba 1.105 last;
9800     } else {
9801    
9802     }
9803     }
9804    
9805     $self->{insertion_mode} = AFTER_BODY_IM;
9806     $token = $self->_get_next_token;
9807 wakaba 1.123 next B;
9808 wakaba 1.54 } elsif ($token->{tag_name} eq 'html') {
9809 wakaba 1.120 ## TODO: Update this code. It seems that the code below is not
9810     ## up-to-date, though it has same effect as speced.
9811 wakaba 1.121 if (@{$self->{open_elements}} > 1 and
9812     $self->{open_elements}->[1]->[1] & BODY_EL) {
9813 wakaba 1.54 ## ISSUE: There is an issue in the spec.
9814 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
9815 wakaba 1.79
9816 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9817     text => $self->{open_elements}->[1]->[0]
9818 wakaba 1.120 ->manakai_local_name,
9819     token => $token);
9820 wakaba 1.79 } else {
9821    
9822 wakaba 1.54 }
9823 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
9824 wakaba 1.54 ## reprocess
9825 wakaba 1.123 next B;
9826 wakaba 1.54 } else {
9827 wakaba 1.79
9828 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
9829     text => $token->{tag_name}, token => $token);
9830 wakaba 1.54 ## Ignore the token
9831     $token = $self->_get_next_token;
9832 wakaba 1.123 next B;
9833 wakaba 1.53 }
9834 wakaba 1.54 } elsif ({
9835     address => 1, blockquote => 1, center => 1, dir => 1,
9836     div => 1, dl => 1, fieldset => 1, listing => 1,
9837     menu => 1, ol => 1, pre => 1, ul => 1,
9838     dd => 1, dt => 1, li => 1,
9839 wakaba 1.102 applet => 1, button => 1, marquee => 1, object => 1,
9840 wakaba 1.54 }->{$token->{tag_name}}) {
9841     ## has an element in scope
9842     my $i;
9843     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9844     my $node = $self->{open_elements}->[$_];
9845 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
9846 wakaba 1.79
9847 wakaba 1.54 $i = $_;
9848 wakaba 1.87 last INSCOPE;
9849 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9850 wakaba 1.79
9851 wakaba 1.54 last INSCOPE;
9852     }
9853     } # INSCOPE
9854 wakaba 1.89
9855     unless (defined $i) { # has an element in scope
9856    
9857 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
9858     text => $token->{tag_name}, token => $token);
9859 wakaba 1.154 ## NOTE: Ignore the token.
9860 wakaba 1.89 } else {
9861     ## Step 1. generate implied end tags
9862     while ({
9863 wakaba 1.149 ## END_TAG_OPTIONAL_EL
9864 wakaba 1.89 dd => ($token->{tag_name} ne 'dd'),
9865     dt => ($token->{tag_name} ne 'dt'),
9866     li => ($token->{tag_name} ne 'li'),
9867     p => 1,
9868 wakaba 1.149 rt => 1,
9869     rp => 1,
9870 wakaba 1.121 }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
9871 wakaba 1.89
9872     pop @{$self->{open_elements}};
9873     }
9874    
9875     ## Step 2.
9876 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
9877     ne $token->{tag_name}) {
9878 wakaba 1.79
9879 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9880     text => $self->{open_elements}->[-1]->[0]
9881 wakaba 1.120 ->manakai_local_name,
9882     token => $token);
9883 wakaba 1.1 } else {
9884 wakaba 1.79
9885 wakaba 1.1 }
9886 wakaba 1.89
9887     ## Step 3.
9888 wakaba 1.54 splice @{$self->{open_elements}}, $i;
9889 wakaba 1.89
9890     ## Step 4.
9891     $clear_up_to_marker->()
9892     if {
9893 wakaba 1.102 applet => 1, button => 1, marquee => 1, object => 1,
9894 wakaba 1.89 }->{$token->{tag_name}};
9895 wakaba 1.54 }
9896     $token = $self->_get_next_token;
9897 wakaba 1.123 next B;
9898 wakaba 1.54 } elsif ($token->{tag_name} eq 'form') {
9899 wakaba 1.92 undef $self->{form_element};
9900    
9901 wakaba 1.54 ## has an element in scope
9902 wakaba 1.92 my $i;
9903 wakaba 1.54 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9904     my $node = $self->{open_elements}->[$_];
9905 wakaba 1.121 if ($node->[1] & FORM_EL) {
9906 wakaba 1.79
9907 wakaba 1.92 $i = $_;
9908 wakaba 1.54 last INSCOPE;
9909 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9910 wakaba 1.79
9911 wakaba 1.54 last INSCOPE;
9912 wakaba 1.36 }
9913 wakaba 1.54 } # INSCOPE
9914 wakaba 1.92
9915     unless (defined $i) { # has an element in scope
9916 wakaba 1.79
9917 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
9918     text => $token->{tag_name}, token => $token);
9919 wakaba 1.154 ## NOTE: Ignore the token.
9920 wakaba 1.54 } else {
9921 wakaba 1.92 ## Step 1. generate implied end tags
9922 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
9923 wakaba 1.92
9924     pop @{$self->{open_elements}};
9925     }
9926    
9927     ## Step 2.
9928 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
9929     ne $token->{tag_name}) {
9930 wakaba 1.92
9931 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9932     text => $self->{open_elements}->[-1]->[0]
9933 wakaba 1.120 ->manakai_local_name,
9934     token => $token);
9935 wakaba 1.92 } else {
9936    
9937     }
9938 wakaba 1.79
9939 wakaba 1.92 ## Step 3.
9940     splice @{$self->{open_elements}}, $i;
9941 wakaba 1.36 }
9942    
9943 wakaba 1.54 $token = $self->_get_next_token;
9944 wakaba 1.123 next B;
9945 wakaba 1.54 } elsif ({
9946     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
9947     }->{$token->{tag_name}}) {
9948     ## has an element in scope
9949     my $i;
9950     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9951     my $node = $self->{open_elements}->[$_];
9952 wakaba 1.121 if ($node->[1] & HEADING_EL) {
9953 wakaba 1.79
9954 wakaba 1.54 $i = $_;
9955     last INSCOPE;
9956 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9957 wakaba 1.79
9958 wakaba 1.54 last INSCOPE;
9959 wakaba 1.53 }
9960 wakaba 1.54 } # INSCOPE
9961 wakaba 1.93
9962     unless (defined $i) { # has an element in scope
9963 wakaba 1.79
9964 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
9965     text => $token->{tag_name}, token => $token);
9966 wakaba 1.154 ## NOTE: Ignore the token.
9967 wakaba 1.79 } else {
9968 wakaba 1.93 ## Step 1. generate implied end tags
9969 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
9970 wakaba 1.93
9971     pop @{$self->{open_elements}};
9972     }
9973 wakaba 1.79
9974 wakaba 1.93 ## Step 2.
9975 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
9976     ne $token->{tag_name}) {
9977 wakaba 1.93
9978 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
9979     text => $token->{tag_name}, token => $token);
9980 wakaba 1.93 } else {
9981    
9982     }
9983    
9984     ## Step 3.
9985     splice @{$self->{open_elements}}, $i;
9986 wakaba 1.53 }
9987    
9988 wakaba 1.54 $token = $self->_get_next_token;
9989 wakaba 1.123 next B;
9990 wakaba 1.87 } elsif ($token->{tag_name} eq 'p') {
9991     ## has an element in scope
9992     my $i;
9993     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9994     my $node = $self->{open_elements}->[$_];
9995 wakaba 1.121 if ($node->[1] & P_EL) {
9996 wakaba 1.87
9997     $i = $_;
9998 wakaba 1.88 last INSCOPE;
9999 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10000 wakaba 1.87
10001     last INSCOPE;
10002     }
10003     } # INSCOPE
10004 wakaba 1.91
10005     if (defined $i) {
10006 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10007     ne $token->{tag_name}) {
10008 wakaba 1.87
10009 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10010     text => $self->{open_elements}->[-1]->[0]
10011 wakaba 1.120 ->manakai_local_name,
10012     token => $token);
10013 wakaba 1.87 } else {
10014    
10015     }
10016 wakaba 1.91
10017 wakaba 1.87 splice @{$self->{open_elements}}, $i;
10018     } else {
10019    
10020 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10021     text => $token->{tag_name}, token => $token);
10022 wakaba 1.91
10023    
10024 wakaba 1.87 ## As if <p>, then reprocess the current token
10025     my $el;
10026    
10027     $el = $self->{document}->create_element_ns
10028 wakaba 1.128 ($HTML_NS, [undef, 'p']);
10029 wakaba 1.87
10030 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
10031     if defined $token->{line};
10032     $el->set_user_data (manakai_source_column => $token->{column})
10033     if defined $token->{column};
10034    
10035 wakaba 1.87 $insert->($el);
10036 wakaba 1.91 ## NOTE: Not inserted into |$self->{open_elements}|.
10037 wakaba 1.87 }
10038 wakaba 1.91
10039 wakaba 1.87 $token = $self->_get_next_token;
10040 wakaba 1.123 next B;
10041 wakaba 1.54 } elsif ({
10042     a => 1,
10043     b => 1, big => 1, em => 1, font => 1, i => 1,
10044     nobr => 1, s => 1, small => 1, strile => 1,
10045     strong => 1, tt => 1, u => 1,
10046     }->{$token->{tag_name}}) {
10047 wakaba 1.79
10048 wakaba 1.111 $formatting_end_tag->($token);
10049 wakaba 1.123 next B;
10050 wakaba 1.54 } elsif ($token->{tag_name} eq 'br') {
10051 wakaba 1.79
10052 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10053     text => 'br', token => $token);
10054 wakaba 1.53
10055 wakaba 1.54 ## As if <br>
10056     $reconstruct_active_formatting_elements->($insert_to_current);
10057    
10058     my $el;
10059    
10060 wakaba 1.1 $el = $self->{document}->create_element_ns
10061 wakaba 1.128 ($HTML_NS, [undef, 'br']);
10062 wakaba 1.1
10063 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
10064     if defined $token->{line};
10065     $el->set_user_data (manakai_source_column => $token->{column})
10066     if defined $token->{column};
10067    
10068 wakaba 1.54 $insert->($el);
10069    
10070     ## Ignore the token.
10071     $token = $self->_get_next_token;
10072 wakaba 1.123 next B;
10073 wakaba 1.54 } elsif ({
10074     caption => 1, col => 1, colgroup => 1, frame => 1,
10075     frameset => 1, head => 1, option => 1, optgroup => 1,
10076     tbody => 1, td => 1, tfoot => 1, th => 1,
10077     thead => 1, tr => 1,
10078     area => 1, basefont => 1, bgsound => 1,
10079     embed => 1, hr => 1, iframe => 1, image => 1,
10080     img => 1, input => 1, isindex => 1, noembed => 1,
10081     noframes => 1, param => 1, select => 1, spacer => 1,
10082     table => 1, textarea => 1, wbr => 1,
10083     noscript => 0, ## TODO: if scripting is enabled
10084     }->{$token->{tag_name}}) {
10085 wakaba 1.79
10086 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10087     text => $token->{tag_name}, token => $token);
10088 wakaba 1.54 ## Ignore the token
10089     $token = $self->_get_next_token;
10090 wakaba 1.123 next B;
10091 wakaba 1.54
10092     ## ISSUE: Issue on HTML5 new elements in spec
10093    
10094     } else {
10095     ## Step 1
10096     my $node_i = -1;
10097     my $node = $self->{open_elements}->[$node_i];
10098    
10099     ## Step 2
10100     S2: {
10101 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
10102 wakaba 1.54 ## Step 1
10103     ## generate implied end tags
10104 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
10105 wakaba 1.79
10106 wakaba 1.149 ## NOTE: |<ruby><rt></ruby>|.
10107     ## ISSUE: <ruby><rt></rt> will also take this code path,
10108     ## which seems wrong.
10109 wakaba 1.86 pop @{$self->{open_elements}};
10110 wakaba 1.149 $node_i++;
10111 wakaba 1.54 }
10112    
10113     ## Step 2
10114 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10115     ne $token->{tag_name}) {
10116 wakaba 1.79
10117 wakaba 1.60 ## NOTE: <x><y></x>
10118 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10119     text => $self->{open_elements}->[-1]->[0]
10120 wakaba 1.120 ->manakai_local_name,
10121     token => $token);
10122 wakaba 1.79 } else {
10123    
10124 wakaba 1.54 }
10125    
10126     ## Step 3
10127 wakaba 1.149 splice @{$self->{open_elements}}, $node_i if $node_i < 0;
10128 wakaba 1.53
10129 wakaba 1.36 $token = $self->_get_next_token;
10130 wakaba 1.54 last S2;
10131 wakaba 1.1 } else {
10132 wakaba 1.54 ## Step 3
10133 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
10134 wakaba 1.54 #not $phrasing_category->{$node->[1]} and
10135 wakaba 1.121 ($node->[1] & SPECIAL_EL or
10136     $node->[1] & SCOPING_EL)) {
10137 wakaba 1.79
10138 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10139     text => $token->{tag_name}, token => $token);
10140 wakaba 1.54 ## Ignore the token
10141     $token = $self->_get_next_token;
10142     last S2;
10143     }
10144 wakaba 1.79
10145    
10146 wakaba 1.1 }
10147 wakaba 1.54
10148     ## Step 4
10149     $node_i--;
10150     $node = $self->{open_elements}->[$node_i];
10151    
10152     ## Step 5;
10153     redo S2;
10154     } # S2
10155 wakaba 1.123 next B;
10156 wakaba 1.1 }
10157     }
10158 wakaba 1.123 next B;
10159     } continue { # B
10160     if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
10161     ## NOTE: The code below is executed in cases where it does not have
10162     ## to be, but it it is harmless even in those cases.
10163     ## has an element in scope
10164     INSCOPE: {
10165     for (reverse 0..$#{$self->{open_elements}}) {
10166     my $node = $self->{open_elements}->[$_];
10167     if ($node->[1] & FOREIGN_EL) {
10168     last INSCOPE;
10169     } elsif ($node->[1] & SCOPING_EL) {
10170     last;
10171     }
10172     }
10173    
10174     ## NOTE: No foreign element in scope.
10175     $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
10176     } # INSCOPE
10177     }
10178 wakaba 1.1 } # B
10179    
10180     ## Stop parsing # MUST
10181    
10182     ## TODO: script stuffs
10183 wakaba 1.3 } # _tree_construct_main
10184    
10185 wakaba 1.173 sub set_inner_html ($$$$;$) {
10186 wakaba 1.3 my $class = shift;
10187     my $node = shift;
10188 wakaba 1.173 #my $s = \$_[0];
10189 wakaba 1.3 my $onerror = $_[1];
10190 wakaba 1.158 my $get_wrapper = $_[2] || sub ($) { return $_[0] };
10191 wakaba 1.3
10192 wakaba 1.65 ## ISSUE: Should {confident} be true?
10193    
10194 wakaba 1.3 my $nt = $node->node_type;
10195     if ($nt == 9) {
10196     # MUST
10197    
10198     ## Step 1 # MUST
10199     ## TODO: If the document has an active parser, ...
10200     ## ISSUE: There is an issue in the spec.
10201    
10202     ## Step 2 # MUST
10203     my @cn = @{$node->child_nodes};
10204     for (@cn) {
10205     $node->remove_child ($_);
10206     }
10207    
10208     ## Step 3, 4, 5 # MUST
10209 wakaba 1.173 $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
10210 wakaba 1.3 } elsif ($nt == 1) {
10211     ## TODO: If non-html element
10212    
10213     ## NOTE: Most of this code is copied from |parse_string|
10214    
10215 wakaba 1.158 ## TODO: Support for $get_wrapper
10216    
10217 wakaba 1.3 ## Step 1 # MUST
10218 wakaba 1.14 my $this_doc = $node->owner_document;
10219     my $doc = $this_doc->implementation->create_document;
10220 wakaba 1.18 $doc->manakai_is_html (1);
10221 wakaba 1.3 my $p = $class->new;
10222     $p->{document} = $doc;
10223    
10224 wakaba 1.84 ## Step 8 # MUST
10225 wakaba 1.3 my $i = 0;
10226 wakaba 1.119 $p->{line_prev} = $p->{line} = 1;
10227     $p->{column_prev} = $p->{column} = 0;
10228 wakaba 1.173 require Whatpm::Charset::DecodeHandle;
10229     my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
10230     $input = $get_wrapper->($input);
10231 wakaba 1.179 $p->{set_nc} = sub {
10232 wakaba 1.3 my $self = shift;
10233 wakaba 1.14
10234 wakaba 1.174 my $char = '';
10235 wakaba 1.179 if (defined $self->{next_nc}) {
10236     $char = $self->{next_nc};
10237     delete $self->{next_nc};
10238     $self->{nc} = ord $char;
10239 wakaba 1.173 } else {
10240 wakaba 1.176 $self->{char_buffer} = '';
10241     $self->{char_buffer_pos} = 0;
10242    
10243     my $count = $input->manakai_read_until
10244 wakaba 1.178 ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
10245     $self->{char_buffer_pos});
10246 wakaba 1.176 if ($count) {
10247     $self->{line_prev} = $self->{line};
10248     $self->{column_prev} = $self->{column};
10249     $self->{column}++;
10250 wakaba 1.179 $self->{nc}
10251 wakaba 1.176 = ord substr ($self->{char_buffer},
10252     $self->{char_buffer_pos}++, 1);
10253     return;
10254     }
10255    
10256 wakaba 1.174 if ($input->read ($char, 1)) {
10257 wakaba 1.179 $self->{nc} = ord $char;
10258 wakaba 1.174 } else {
10259 wakaba 1.179 $self->{nc} = -1;
10260 wakaba 1.174 return;
10261     }
10262 wakaba 1.173 }
10263 wakaba 1.119
10264     ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
10265     $p->{column}++;
10266 wakaba 1.4
10267 wakaba 1.179 if ($self->{nc} == 0x000A) { # LF
10268 wakaba 1.119 $p->{line}++;
10269     $p->{column} = 0;
10270 wakaba 1.79
10271 wakaba 1.179 } elsif ($self->{nc} == 0x000D) { # CR
10272 wakaba 1.173 ## TODO: support for abort/streaming
10273 wakaba 1.174 my $next = '';
10274     if ($input->read ($next, 1) and $next ne "\x0A") {
10275 wakaba 1.179 $self->{next_nc} = $next;
10276 wakaba 1.173 }
10277 wakaba 1.179 $self->{nc} = 0x000A; # LF # MUST
10278 wakaba 1.119 $p->{line}++;
10279     $p->{column} = 0;
10280 wakaba 1.79
10281 wakaba 1.179 } elsif ($self->{nc} == 0x0000) { # NULL
10282 wakaba 1.79
10283 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'NULL');
10284 wakaba 1.179 $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
10285 wakaba 1.3 }
10286     };
10287 wakaba 1.167
10288 wakaba 1.168 $p->{read_until} = sub {
10289 wakaba 1.173 #my ($scalar, $specials_range, $offset) = @_;
10290 wakaba 1.179 return 0 if defined $p->{next_nc};
10291 wakaba 1.176
10292 wakaba 1.178 my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
10293 wakaba 1.176 my $offset = $_[2] || 0;
10294    
10295     if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
10296     pos ($p->{char_buffer}) = $p->{char_buffer_pos};
10297     if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
10298     substr ($_[0], $offset)
10299     = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
10300     my $count = $+[0] - $-[0];
10301     if ($count) {
10302     $p->{column} += $count;
10303     $p->{char_buffer_pos} += $count;
10304     $p->{line_prev} = $p->{line};
10305     $p->{column_prev} = $p->{column} - 1;
10306 wakaba 1.179 $p->{nc} = -1;
10307 wakaba 1.176 }
10308     return $count;
10309     } else {
10310     return 0;
10311     }
10312     } else {
10313     my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
10314     if ($count) {
10315     $p->{column} += $count;
10316     $p->{column_prev} += $count;
10317 wakaba 1.179 $p->{nc} = -1;
10318 wakaba 1.176 }
10319     return $count;
10320 wakaba 1.173 }
10321     }; # $p->{read_until}
10322 wakaba 1.167
10323 wakaba 1.3 my $ponerror = $onerror || sub {
10324     my (%opt) = @_;
10325 wakaba 1.119 my $line = $opt{line};
10326     my $column = $opt{column};
10327     if (defined $opt{token} and defined $opt{token}->{line}) {
10328     $line = $opt{token}->{line};
10329     $column = $opt{token}->{column};
10330     }
10331     warn "Parse error ($opt{type}) at line $line column $column\n";
10332 wakaba 1.3 };
10333     $p->{parse_error} = sub {
10334 wakaba 1.119 $ponerror->(line => $p->{line}, column => $p->{column}, @_);
10335 wakaba 1.3 };
10336    
10337 wakaba 1.174 my $char_onerror = sub {
10338     my (undef, $type, %opt) = @_;
10339     $ponerror->(layer => 'encode',
10340     line => $p->{line}, column => $p->{column} + 1,
10341     %opt, type => $type);
10342     }; # $char_onerror
10343     $input->onerror ($char_onerror);
10344    
10345 wakaba 1.3 $p->_initialize_tokenizer;
10346     $p->_initialize_tree_constructor;
10347    
10348     ## Step 2
10349 wakaba 1.71 my $node_ln = $node->manakai_local_name;
10350 wakaba 1.41 $p->{content_model} = {
10351     title => RCDATA_CONTENT_MODEL,
10352     textarea => RCDATA_CONTENT_MODEL,
10353     style => CDATA_CONTENT_MODEL,
10354     script => CDATA_CONTENT_MODEL,
10355     xmp => CDATA_CONTENT_MODEL,
10356     iframe => CDATA_CONTENT_MODEL,
10357     noembed => CDATA_CONTENT_MODEL,
10358     noframes => CDATA_CONTENT_MODEL,
10359     noscript => CDATA_CONTENT_MODEL,
10360     plaintext => PLAINTEXT_CONTENT_MODEL,
10361     }->{$node_ln};
10362     $p->{content_model} = PCDATA_CONTENT_MODEL
10363     unless defined $p->{content_model};
10364     ## ISSUE: What is "the name of the element"? local name?
10365 wakaba 1.3
10366 wakaba 1.121 $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
10367     ## TODO: Foreign element OK?
10368 wakaba 1.3
10369 wakaba 1.84 ## Step 3
10370 wakaba 1.3 my $root = $doc->create_element_ns
10371     ('http://www.w3.org/1999/xhtml', [undef, 'html']);
10372    
10373 wakaba 1.84 ## Step 4 # MUST
10374 wakaba 1.3 $doc->append_child ($root);
10375    
10376 wakaba 1.84 ## Step 5 # MUST
10377 wakaba 1.121 push @{$p->{open_elements}}, [$root, $el_category->{html}];
10378 wakaba 1.3
10379     undef $p->{head_element};
10380    
10381 wakaba 1.84 ## Step 6 # MUST
10382 wakaba 1.3 $p->_reset_insertion_mode;
10383    
10384 wakaba 1.84 ## Step 7 # MUST
10385 wakaba 1.3 my $anode = $node;
10386     AN: while (defined $anode) {
10387     if ($anode->node_type == 1) {
10388     my $nsuri = $anode->namespace_uri;
10389     if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
10390 wakaba 1.71 if ($anode->manakai_local_name eq 'form') {
10391 wakaba 1.79
10392 wakaba 1.3 $p->{form_element} = $anode;
10393     last AN;
10394     }
10395     }
10396     }
10397     $anode = $anode->parent_node;
10398     } # AN
10399    
10400 wakaba 1.84 ## Step 9 # MUST
10401 wakaba 1.3 {
10402     my $self = $p;
10403     $token = $self->_get_next_token;
10404     }
10405     $p->_tree_construction_main;
10406    
10407 wakaba 1.84 ## Step 10 # MUST
10408 wakaba 1.3 my @cn = @{$node->child_nodes};
10409     for (@cn) {
10410     $node->remove_child ($_);
10411     }
10412     ## ISSUE: mutation events? read-only?
10413    
10414 wakaba 1.84 ## Step 11 # MUST
10415 wakaba 1.3 @cn = @{$root->child_nodes};
10416     for (@cn) {
10417 wakaba 1.14 $this_doc->adopt_node ($_);
10418 wakaba 1.3 $node->append_child ($_);
10419     }
10420 wakaba 1.14 ## ISSUE: mutation events?
10421 wakaba 1.3
10422     $p->_terminate_tree_constructor;
10423 wakaba 1.119
10424     delete $p->{parse_error}; # delete loop
10425 wakaba 1.3 } else {
10426     die "$0: |set_inner_html| is not defined for node of type $nt";
10427     }
10428     } # set_inner_html
10429    
10430     } # tree construction stage
10431 wakaba 1.1
10432 wakaba 1.65 package Whatpm::HTML::RestartParser;
10433     push our @ISA, 'Error';
10434    
10435 wakaba 1.1 1;
10436 wakaba 1.180 # $Date: 2008/09/15 08:09:39 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24