/[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.165 - (hide annotations) (download)
Sat Sep 13 11:31:08 2008 UTC (17 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.164: +213 -805 lines
++ whatpm/Whatpm/ChangeLog	13 Sep 2008 11:31:02 -0000
	* HTML.pm.src: Remove |{char}|, which is no longer used.
	Remove |{entity_in_attr}| and |{last_attribute_value_state}|
	and replaced by |{prev_state}|.

	* mkhtmlparser.pl: Remove |{char}| feature.
	Remove |!!!back-next-input-character;| macro.

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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24