/[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.174 - (hide annotations) (download)
Sun Sep 14 11:57:41 2008 UTC (17 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.173: +35 -16 lines
++ whatpm/Whatpm/ChangeLog	14 Sep 2008 11:56:24 -0000
	* HTML.pm.src: Use |read| instead of |getc|.  |set_inner_html|
	would report character error from now.

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

++ whatpm/Whatpm/Charset/ChangeLog	14 Sep 2008 11:57:38 -0000
	* DecodeHandle.pm (CharString onerror): New method.

	* UnicodeString.pm (read): New.
	(getc): Removed.
	(manakai_read_until): Checking operation implemented.

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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24