/[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.200 - (hide annotations) (download)
Mon Oct 13 06:18:30 2008 UTC (17 years, 9 months ago) by wakaba
Branch: MAIN
Changes since 1.199: +140 -131 lines
++ whatpm/t/ChangeLog	13 Oct 2008 06:18:26 -0000
2008-10-13  Wakaba  <wakaba@suika.fam.cx>

	* tokenizer-test-2.dat: A test result was wrong.

++ whatpm/Whatpm/ChangeLog	13 Oct 2008 06:17:59 -0000
2008-10-13  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm.src: Steps for CDATA/RCDATA elements in tree
	construction stage synced with the spec (HTML5 revisions 2139 and
	2302).

1 wakaba 1.2 package Whatpm::HTML;
2 wakaba 1.1 use strict;
3 wakaba 1.200 our $VERSION=do{my @r=(q$Revision: 1.204 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 wakaba 1.65 use Error qw(:try);
5 wakaba 1.1
6 wakaba 1.179 ## NOTE: This module don't check all HTML5 parse errors; character
7     ## encoding related parse errors are expected to be handled by relevant
8     ## modules.
9     ## Parse errors for control characters that are not allowed in HTML5
10     ## documents, for surrogate code points, and for noncharacter code
11     ## points, as well as U+FFFD substitions for characters whose code points
12     ## is higher than U+10FFFF may be detected by combining the parser with
13     ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14     ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15     ## WebHACC::Language::HTML module in the WebHACC package).
16    
17 wakaba 1.18 ## ISSUE:
18     ## var doc = implementation.createDocument (null, null, null);
19     ## doc.write ('');
20     ## alert (doc.compatMode);
21 wakaba 1.1
22 wakaba 1.136 require IO::Handle;
23    
24 wakaba 1.123 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25     my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26     my $SVG_NS = q<http://www.w3.org/2000/svg>;
27     my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28     my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29     my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    
31 wakaba 1.121 sub A_EL () { 0b1 }
32     sub ADDRESS_EL () { 0b10 }
33     sub BODY_EL () { 0b100 }
34     sub BUTTON_EL () { 0b1000 }
35     sub CAPTION_EL () { 0b10000 }
36     sub DD_EL () { 0b100000 }
37     sub DIV_EL () { 0b1000000 }
38     sub DT_EL () { 0b10000000 }
39     sub FORM_EL () { 0b100000000 }
40     sub FORMATTING_EL () { 0b1000000000 }
41     sub FRAMESET_EL () { 0b10000000000 }
42     sub HEADING_EL () { 0b100000000000 }
43     sub HTML_EL () { 0b1000000000000 }
44     sub LI_EL () { 0b10000000000000 }
45     sub NOBR_EL () { 0b100000000000000 }
46     sub OPTION_EL () { 0b1000000000000000 }
47     sub OPTGROUP_EL () { 0b10000000000000000 }
48     sub P_EL () { 0b100000000000000000 }
49     sub SELECT_EL () { 0b1000000000000000000 }
50     sub TABLE_EL () { 0b10000000000000000000 }
51     sub TABLE_CELL_EL () { 0b100000000000000000000 }
52     sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53     sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54     sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55     sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56 wakaba 1.123 sub FOREIGN_EL () { 0b10000000000000000000000000 }
57     sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58     sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59 wakaba 1.149 sub RUBY_EL () { 0b10000000000000000000000000000 }
60     sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61 wakaba 1.121
62     sub TABLE_ROWS_EL () {
63     TABLE_EL |
64     TABLE_ROW_EL |
65     TABLE_ROW_GROUP_EL
66     }
67    
68 wakaba 1.149 ## NOTE: Used in "generate implied end tags" algorithm.
69 wakaba 1.189 ## NOTE: There is a code where a modified version of
70     ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71     ## implementation (search for the algorithm name).
72 wakaba 1.121 sub END_TAG_OPTIONAL_EL () {
73     DD_EL |
74     DT_EL |
75     LI_EL |
76 wakaba 1.189 OPTION_EL |
77     OPTGROUP_EL |
78 wakaba 1.149 P_EL |
79     RUBY_COMPONENT_EL
80 wakaba 1.121 }
81    
82 wakaba 1.149 ## NOTE: Used in </body> and EOF algorithms.
83 wakaba 1.121 sub ALL_END_TAG_OPTIONAL_EL () {
84 wakaba 1.149 DD_EL |
85     DT_EL |
86     LI_EL |
87     P_EL |
88    
89 wakaba 1.196 ## ISSUE: option, optgroup, rt, rp?
90    
91 wakaba 1.121 BODY_EL |
92     HTML_EL |
93     TABLE_CELL_EL |
94     TABLE_ROW_EL |
95     TABLE_ROW_GROUP_EL
96     }
97    
98     sub SCOPING_EL () {
99     BUTTON_EL |
100     CAPTION_EL |
101     HTML_EL |
102     TABLE_EL |
103     TABLE_CELL_EL |
104     MISC_SCOPING_EL
105     }
106    
107     sub TABLE_SCOPING_EL () {
108     HTML_EL |
109     TABLE_EL
110     }
111    
112     sub TABLE_ROWS_SCOPING_EL () {
113     HTML_EL |
114     TABLE_ROW_GROUP_EL
115     }
116    
117     sub TABLE_ROW_SCOPING_EL () {
118     HTML_EL |
119     TABLE_ROW_EL
120     }
121    
122     sub SPECIAL_EL () {
123     ADDRESS_EL |
124     BODY_EL |
125     DIV_EL |
126 wakaba 1.149
127     DD_EL |
128     DT_EL |
129     LI_EL |
130     P_EL |
131    
132 wakaba 1.121 FORM_EL |
133     FRAMESET_EL |
134     HEADING_EL |
135     SELECT_EL |
136     TABLE_ROW_EL |
137     TABLE_ROW_GROUP_EL |
138     MISC_SPECIAL_EL
139     }
140    
141     my $el_category = {
142     a => A_EL | FORMATTING_EL,
143     address => ADDRESS_EL,
144     applet => MISC_SCOPING_EL,
145     area => MISC_SPECIAL_EL,
146 wakaba 1.188 article => MISC_SPECIAL_EL,
147     aside => MISC_SPECIAL_EL,
148 wakaba 1.121 b => FORMATTING_EL,
149     base => MISC_SPECIAL_EL,
150     basefont => MISC_SPECIAL_EL,
151     bgsound => MISC_SPECIAL_EL,
152     big => FORMATTING_EL,
153     blockquote => MISC_SPECIAL_EL,
154     body => BODY_EL,
155     br => MISC_SPECIAL_EL,
156     button => BUTTON_EL,
157     caption => CAPTION_EL,
158     center => MISC_SPECIAL_EL,
159     col => MISC_SPECIAL_EL,
160     colgroup => MISC_SPECIAL_EL,
161 wakaba 1.188 command => MISC_SPECIAL_EL,
162     datagrid => MISC_SPECIAL_EL,
163 wakaba 1.121 dd => DD_EL,
164 wakaba 1.188 details => MISC_SPECIAL_EL,
165     dialog => MISC_SPECIAL_EL,
166 wakaba 1.121 dir => MISC_SPECIAL_EL,
167     div => DIV_EL,
168     dl => MISC_SPECIAL_EL,
169     dt => DT_EL,
170     em => FORMATTING_EL,
171     embed => MISC_SPECIAL_EL,
172 wakaba 1.188 eventsource => MISC_SPECIAL_EL,
173 wakaba 1.121 fieldset => MISC_SPECIAL_EL,
174 wakaba 1.188 figure => MISC_SPECIAL_EL,
175 wakaba 1.121 font => FORMATTING_EL,
176 wakaba 1.188 footer => MISC_SPECIAL_EL,
177 wakaba 1.121 form => FORM_EL,
178     frame => MISC_SPECIAL_EL,
179     frameset => FRAMESET_EL,
180     h1 => HEADING_EL,
181     h2 => HEADING_EL,
182     h3 => HEADING_EL,
183     h4 => HEADING_EL,
184     h5 => HEADING_EL,
185     h6 => HEADING_EL,
186     head => MISC_SPECIAL_EL,
187 wakaba 1.188 header => MISC_SPECIAL_EL,
188 wakaba 1.121 hr => MISC_SPECIAL_EL,
189     html => HTML_EL,
190     i => FORMATTING_EL,
191     iframe => MISC_SPECIAL_EL,
192     img => MISC_SPECIAL_EL,
193 wakaba 1.188 #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194 wakaba 1.121 input => MISC_SPECIAL_EL,
195     isindex => MISC_SPECIAL_EL,
196     li => LI_EL,
197     link => MISC_SPECIAL_EL,
198     listing => MISC_SPECIAL_EL,
199     marquee => MISC_SCOPING_EL,
200     menu => MISC_SPECIAL_EL,
201     meta => MISC_SPECIAL_EL,
202 wakaba 1.188 nav => MISC_SPECIAL_EL,
203 wakaba 1.121 nobr => NOBR_EL | FORMATTING_EL,
204     noembed => MISC_SPECIAL_EL,
205     noframes => MISC_SPECIAL_EL,
206     noscript => MISC_SPECIAL_EL,
207     object => MISC_SCOPING_EL,
208     ol => MISC_SPECIAL_EL,
209     optgroup => OPTGROUP_EL,
210     option => OPTION_EL,
211     p => P_EL,
212     param => MISC_SPECIAL_EL,
213     plaintext => MISC_SPECIAL_EL,
214     pre => MISC_SPECIAL_EL,
215 wakaba 1.149 rp => RUBY_COMPONENT_EL,
216     rt => RUBY_COMPONENT_EL,
217     ruby => RUBY_EL,
218 wakaba 1.121 s => FORMATTING_EL,
219     script => MISC_SPECIAL_EL,
220     select => SELECT_EL,
221 wakaba 1.188 section => MISC_SPECIAL_EL,
222 wakaba 1.121 small => FORMATTING_EL,
223     spacer => MISC_SPECIAL_EL,
224     strike => FORMATTING_EL,
225     strong => FORMATTING_EL,
226     style => MISC_SPECIAL_EL,
227     table => TABLE_EL,
228     tbody => TABLE_ROW_GROUP_EL,
229     td => TABLE_CELL_EL,
230     textarea => MISC_SPECIAL_EL,
231     tfoot => TABLE_ROW_GROUP_EL,
232     th => TABLE_CELL_EL,
233     thead => TABLE_ROW_GROUP_EL,
234     title => MISC_SPECIAL_EL,
235     tr => TABLE_ROW_EL,
236     tt => FORMATTING_EL,
237     u => FORMATTING_EL,
238     ul => MISC_SPECIAL_EL,
239     wbr => MISC_SPECIAL_EL,
240     };
241    
242 wakaba 1.123 my $el_category_f = {
243     $MML_NS => {
244     'annotation-xml' => MML_AXML_EL,
245     mi => FOREIGN_FLOW_CONTENT_EL,
246     mo => FOREIGN_FLOW_CONTENT_EL,
247     mn => FOREIGN_FLOW_CONTENT_EL,
248     ms => FOREIGN_FLOW_CONTENT_EL,
249     mtext => FOREIGN_FLOW_CONTENT_EL,
250     },
251     $SVG_NS => {
252 wakaba 1.195 foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253 wakaba 1.123 desc => FOREIGN_FLOW_CONTENT_EL,
254     title => FOREIGN_FLOW_CONTENT_EL,
255     },
256     ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257     };
258    
259 wakaba 1.128 my $svg_attr_name = {
260 wakaba 1.143 attributename => 'attributeName',
261 wakaba 1.128 attributetype => 'attributeType',
262     basefrequency => 'baseFrequency',
263     baseprofile => 'baseProfile',
264     calcmode => 'calcMode',
265     clippathunits => 'clipPathUnits',
266     contentscripttype => 'contentScriptType',
267     contentstyletype => 'contentStyleType',
268     diffuseconstant => 'diffuseConstant',
269     edgemode => 'edgeMode',
270     externalresourcesrequired => 'externalResourcesRequired',
271     filterres => 'filterRes',
272     filterunits => 'filterUnits',
273     glyphref => 'glyphRef',
274     gradienttransform => 'gradientTransform',
275     gradientunits => 'gradientUnits',
276     kernelmatrix => 'kernelMatrix',
277     kernelunitlength => 'kernelUnitLength',
278     keypoints => 'keyPoints',
279     keysplines => 'keySplines',
280     keytimes => 'keyTimes',
281     lengthadjust => 'lengthAdjust',
282     limitingconeangle => 'limitingConeAngle',
283     markerheight => 'markerHeight',
284     markerunits => 'markerUnits',
285     markerwidth => 'markerWidth',
286     maskcontentunits => 'maskContentUnits',
287     maskunits => 'maskUnits',
288     numoctaves => 'numOctaves',
289     pathlength => 'pathLength',
290     patterncontentunits => 'patternContentUnits',
291     patterntransform => 'patternTransform',
292     patternunits => 'patternUnits',
293     pointsatx => 'pointsAtX',
294     pointsaty => 'pointsAtY',
295     pointsatz => 'pointsAtZ',
296     preservealpha => 'preserveAlpha',
297     preserveaspectratio => 'preserveAspectRatio',
298     primitiveunits => 'primitiveUnits',
299     refx => 'refX',
300     refy => 'refY',
301     repeatcount => 'repeatCount',
302     repeatdur => 'repeatDur',
303     requiredextensions => 'requiredExtensions',
304 wakaba 1.143 requiredfeatures => 'requiredFeatures',
305 wakaba 1.128 specularconstant => 'specularConstant',
306     specularexponent => 'specularExponent',
307     spreadmethod => 'spreadMethod',
308     startoffset => 'startOffset',
309     stddeviation => 'stdDeviation',
310     stitchtiles => 'stitchTiles',
311     surfacescale => 'surfaceScale',
312     systemlanguage => 'systemLanguage',
313     tablevalues => 'tableValues',
314     targetx => 'targetX',
315     targety => 'targetY',
316     textlength => 'textLength',
317     viewbox => 'viewBox',
318     viewtarget => 'viewTarget',
319     xchannelselector => 'xChannelSelector',
320     ychannelselector => 'yChannelSelector',
321     zoomandpan => 'zoomAndPan',
322     };
323    
324     my $foreign_attr_xname = {
325     'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326     'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327     'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328     'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329     'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330     'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331     'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332     'xml:base' => [$XML_NS, ['xml', 'base']],
333     'xml:lang' => [$XML_NS, ['xml', 'lang']],
334     'xml:space' => [$XML_NS, ['xml', 'space']],
335     'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336     'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337     };
338    
339 wakaba 1.129 ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341 wakaba 1.187 my $charref_map = {
342     0x0D => 0x000A,
343 wakaba 1.9 0x80 => 0x20AC,
344     0x81 => 0xFFFD,
345     0x82 => 0x201A,
346     0x83 => 0x0192,
347     0x84 => 0x201E,
348     0x85 => 0x2026,
349     0x86 => 0x2020,
350     0x87 => 0x2021,
351     0x88 => 0x02C6,
352     0x89 => 0x2030,
353     0x8A => 0x0160,
354     0x8B => 0x2039,
355     0x8C => 0x0152,
356     0x8D => 0xFFFD,
357     0x8E => 0x017D,
358     0x8F => 0xFFFD,
359     0x90 => 0xFFFD,
360     0x91 => 0x2018,
361     0x92 => 0x2019,
362     0x93 => 0x201C,
363     0x94 => 0x201D,
364     0x95 => 0x2022,
365     0x96 => 0x2013,
366     0x97 => 0x2014,
367     0x98 => 0x02DC,
368     0x99 => 0x2122,
369     0x9A => 0x0161,
370     0x9B => 0x203A,
371     0x9C => 0x0153,
372     0x9D => 0xFFFD,
373     0x9E => 0x017E,
374     0x9F => 0x0178,
375 wakaba 1.187 }; # $charref_map
376     $charref_map->{$_} = 0xFFFD
377     for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378     0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379     0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380     0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381     0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382     0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383     0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384 wakaba 1.1
385 wakaba 1.188 ## TODO: Invoke the reset algorithm when a resettable element is
386     ## created (cf. HTML5 revision 2259).
387    
388 wakaba 1.65 sub parse_byte_string ($$$$;$) {
389 wakaba 1.135 my $self = shift;
390     my $charset_name = shift;
391     open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392     return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393     } # parse_byte_string
394    
395 wakaba 1.158 sub parse_byte_stream ($$$$;$$) {
396     # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397 wakaba 1.65 my $self = ref $_[0] ? shift : shift->new;
398 wakaba 1.130 my $charset_name = shift;
399 wakaba 1.135 my $byte_stream = $_[0];
400 wakaba 1.130
401 wakaba 1.131 my $onerror = $_[2] || sub {
402     my (%opt) = @_;
403     warn "Parse error ($opt{type})\n";
404     };
405     $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407 wakaba 1.158 my $get_wrapper = $_[3] || sub ($) {
408     return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409     };
410    
411 wakaba 1.130 ## HTML5 encoding sniffing algorithm
412     require Message::Charset::Info;
413     my $charset;
414 wakaba 1.133 my $buffer;
415     my ($char_stream, $e_status);
416 wakaba 1.130
417     SNIFFING: {
418 wakaba 1.156 ## NOTE: By setting |allow_fallback| option true when the
419     ## |get_decode_handle| method is invoked, we ignore what the HTML5
420     ## spec requires, i.e. unsupported encoding should be ignored.
421     ## TODO: We should not do this unless the parser is invoked
422     ## in the conformance checking mode, in which this behavior
423     ## would be useful.
424 wakaba 1.130
425     ## Step 1
426     if (defined $charset_name) {
427 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428     ## TODO: Is this ok? Transfer protocol's parameter should be
429     ## interpreted in its semantics?
430 wakaba 1.130
431 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
432     ($byte_stream, allow_error_reporting => 1,
433 wakaba 1.130 allow_fallback => 1);
434 wakaba 1.133 if ($char_stream) {
435 wakaba 1.130 $self->{confident} = 1;
436     last SNIFFING;
437 wakaba 1.133 } else {
438 wakaba 1.186 $self->{parse_error}->(level => $self->{level}->{must}, type => 'charset:not supported',
439 wakaba 1.187 layer => 'encode',
440 wakaba 1.186 line => 1, column => 1,
441     value => $charset_name,
442     level => $self->{level}->{uncertain});
443 wakaba 1.130 }
444     }
445    
446     ## Step 2
447 wakaba 1.133 my $byte_buffer = '';
448     for (1..1024) {
449     my $char = $byte_stream->getc;
450     last unless defined $char;
451     $byte_buffer .= $char;
452     } ## TODO: timeout
453 wakaba 1.130
454     ## Step 3
455 wakaba 1.133 if ($byte_buffer =~ /^\xFE\xFF/) {
456 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
458     ($byte_stream, allow_error_reporting => 1,
459     allow_fallback => 1, byte_buffer => \$byte_buffer);
460 wakaba 1.130 $self->{confident} = 1;
461     last SNIFFING;
462 wakaba 1.133 } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
465     ($byte_stream, allow_error_reporting => 1,
466     allow_fallback => 1, byte_buffer => \$byte_buffer);
467 wakaba 1.130 $self->{confident} = 1;
468     last SNIFFING;
469 wakaba 1.133 } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
472     ($byte_stream, allow_error_reporting => 1,
473     allow_fallback => 1, byte_buffer => \$byte_buffer);
474 wakaba 1.130 $self->{confident} = 1;
475     last SNIFFING;
476     }
477    
478     ## Step 4
479     ## TODO: <meta charset>
480    
481     ## Step 5
482     ## TODO: from history
483    
484     ## Step 6
485 wakaba 1.67 require Whatpm::Charset::UniversalCharDet;
486 wakaba 1.130 $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487 wakaba 1.133 ($byte_buffer);
488 wakaba 1.130 if (defined $charset_name) {
489 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490 wakaba 1.130
491 wakaba 1.133 require Whatpm::Charset::DecodeHandle;
492     $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493     ($byte_stream);
494     ($char_stream, $e_status) = $charset->get_decode_handle
495     ($buffer, allow_error_reporting => 1,
496     allow_fallback => 1, byte_buffer => \$byte_buffer);
497     if ($char_stream) {
498     $buffer->{buffer} = $byte_buffer;
499 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'sniffing:chardet',
500     text => $charset_name,
501     level => $self->{level}->{info},
502     layer => 'encode',
503 wakaba 1.131 line => 1, column => 1);
504 wakaba 1.130 $self->{confident} = 0;
505     last SNIFFING;
506     }
507     }
508    
509     ## Step 7: default
510     ## TODO: Make this configurable.
511 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512 wakaba 1.130 ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513     ## detectable in the step 6.
514 wakaba 1.133 require Whatpm::Charset::DecodeHandle;
515     $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516     ($byte_stream);
517     ($char_stream, $e_status)
518     = $charset->get_decode_handle ($buffer,
519     allow_error_reporting => 1,
520     allow_fallback => 1,
521     byte_buffer => \$byte_buffer);
522     $buffer->{buffer} = $byte_buffer;
523 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'sniffing:default',
524     text => 'windows-1252',
525     level => $self->{level}->{info},
526     line => 1, column => 1,
527     layer => 'encode');
528 wakaba 1.65 $self->{confident} = 0;
529 wakaba 1.130 } # SNIFFING
530    
531     if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:fallback',
534 wakaba 1.156 #text => $self->{input_encoding},
535 wakaba 1.150 level => $self->{level}->{uncertain},
536     line => 1, column => 1,
537     layer => 'encode');
538 wakaba 1.130 } elsif (not ($e_status &
539 wakaba 1.174 Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name;
541 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:no error',
542     text => $self->{input_encoding},
543     level => $self->{level}->{uncertain},
544     line => 1, column => 1,
545     layer => 'encode');
546 wakaba 1.156 } else {
547     $self->{input_encoding} = $charset->get_iana_name;
548 wakaba 1.65 }
549    
550     $self->{change_encoding} = sub {
551     my $self = shift;
552 wakaba 1.131 $charset_name = shift;
553 wakaba 1.112 my $token = shift;
554 wakaba 1.65
555 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
557     ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558     byte_buffer => \ $buffer->{buffer});
559 wakaba 1.131
560 wakaba 1.133 if ($char_stream) { # if supported
561 wakaba 1.131 ## "Change the encoding" algorithm:
562    
563     ## Step 1
564 wakaba 1.146 if ($charset->{category} &
565     Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566 wakaba 1.157 $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567 wakaba 1.133 ($char_stream, $e_status) = $charset->get_decode_handle
568     ($byte_stream,
569     byte_buffer => \ $buffer->{buffer});
570 wakaba 1.131 }
571     $charset_name = $charset->get_iana_name;
572    
573     ## Step 2
574     if (defined $self->{input_encoding} and
575     $self->{input_encoding} eq $charset_name) {
576 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'charset label:matching',
577     text => $charset_name,
578     level => $self->{level}->{info});
579 wakaba 1.131 $self->{confident} = 1;
580     return;
581     }
582 wakaba 1.65
583 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'charset label detected',
584     text => $self->{input_encoding},
585     value => $charset_name,
586     level => $self->{level}->{warn},
587     token => $token);
588 wakaba 1.131
589     ## Step 3
590     # if (can) {
591     ## change the encoding on the fly.
592     #$self->{confident} = 1;
593     #return;
594     # }
595    
596     ## Step 4
597     throw Whatpm::HTML::RestartParser ();
598 wakaba 1.65 }
599     }; # $self->{change_encoding}
600    
601 wakaba 1.133 my $char_onerror = sub {
602     my (undef, $type, %opt) = @_;
603 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, layer => 'encode',
604 wakaba 1.170 line => $self->{line}, column => $self->{column} + 1,
605     %opt, type => $type);
606 wakaba 1.133 if ($opt{octets}) {
607     ${$opt{octets}} = "\x{FFFD}"; # relacement character
608     }
609     };
610 wakaba 1.158
611     my $wrapped_char_stream = $get_wrapper->($char_stream);
612     $wrapped_char_stream->onerror ($char_onerror);
613 wakaba 1.133
614 wakaba 1.178 my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615 wakaba 1.65 my $return;
616     try {
617 wakaba 1.158 $return = $self->parse_char_stream ($wrapped_char_stream, @args);
618 wakaba 1.65 } catch Whatpm::HTML::RestartParser with {
619 wakaba 1.131 ## NOTE: Invoked after {change_encoding}.
620    
621     if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:fallback',
624     level => $self->{level}->{uncertain},
625 wakaba 1.156 #text => $self->{input_encoding},
626 wakaba 1.150 line => 1, column => 1,
627     layer => 'encode');
628 wakaba 1.131 } elsif (not ($e_status &
629 wakaba 1.174 Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630 wakaba 1.156 $self->{input_encoding} = $charset->get_iana_name;
631 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'chardecode:no error',
632     text => $self->{input_encoding},
633     level => $self->{level}->{uncertain},
634     line => 1, column => 1,
635     layer => 'encode');
636 wakaba 1.156 } else {
637     $self->{input_encoding} = $charset->get_iana_name;
638 wakaba 1.131 }
639 wakaba 1.65 $self->{confident} = 1;
640 wakaba 1.158
641     $wrapped_char_stream = $get_wrapper->($char_stream);
642     $wrapped_char_stream->onerror ($char_onerror);
643    
644     $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645 wakaba 1.65 };
646     return $return;
647 wakaba 1.135 } # parse_byte_stream
648 wakaba 1.65
649 wakaba 1.71 ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650     ## and the HTML layer MUST ignore it. However, we does strip BOM in
651     ## the encoding layer and the HTML layer does not ignore any U+FEFF,
652     ## because the core part of our HTML parser expects a string of character,
653     ## not a string of bytes or code units or anything which might contain a BOM.
654     ## Therefore, any parser interface that accepts a string of bytes,
655     ## such as |parse_byte_string| in this module, must ensure that it does
656     ## strip the BOM and never strip any ZWNBSP.
657    
658 wakaba 1.158 sub parse_char_string ($$$;$$) {
659     #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660 wakaba 1.132 my $self = shift;
661 wakaba 1.136 my $s = ref $_[0] ? $_[0] : \($_[0]);
662 wakaba 1.167 require Whatpm::Charset::DecodeHandle;
663     my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664 wakaba 1.132 return $self->parse_char_stream ($input, @_[1..$#_]);
665     } # parse_char_string
666 wakaba 1.158 *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667 wakaba 1.65
668 wakaba 1.178 sub parse_char_stream ($$$;$$) {
669 wakaba 1.65 my $self = ref $_[0] ? shift : shift->new;
670 wakaba 1.132 my $input = $_[0];
671 wakaba 1.1 $self->{document} = $_[1];
672 wakaba 1.65 @{$self->{document}->child_nodes} = ();
673 wakaba 1.1
674 wakaba 1.3 ## NOTE: |set_inner_html| copies most of this method's code
675    
676 wakaba 1.65 $self->{confident} = 1 unless exists $self->{confident};
677 wakaba 1.66 $self->{document}->input_encoding ($self->{input_encoding})
678     if defined $self->{input_encoding};
679 wakaba 1.174 ## TODO: |{input_encoding}| is needless?
680 wakaba 1.65
681 wakaba 1.110 $self->{line_prev} = $self->{line} = 1;
682 wakaba 1.175 $self->{column_prev} = -1;
683     $self->{column} = 0;
684 wakaba 1.179 $self->{set_nc} = sub {
685 wakaba 1.1 my $self = shift;
686 wakaba 1.13
687 wakaba 1.174 my $char = '';
688 wakaba 1.179 if (defined $self->{next_nc}) {
689     $char = $self->{next_nc};
690     delete $self->{next_nc};
691     $self->{nc} = ord $char;
692 wakaba 1.136 } else {
693 wakaba 1.175 $self->{char_buffer} = '';
694     $self->{char_buffer_pos} = 0;
695    
696     my $count = $input->manakai_read_until
697 wakaba 1.178 ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698 wakaba 1.175 if ($count) {
699     $self->{line_prev} = $self->{line};
700     $self->{column_prev} = $self->{column};
701     $self->{column}++;
702 wakaba 1.179 $self->{nc}
703 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704     return;
705     }
706    
707 wakaba 1.174 if ($input->read ($char, 1)) {
708 wakaba 1.179 $self->{nc} = ord $char;
709 wakaba 1.174 } else {
710 wakaba 1.179 $self->{nc} = -1;
711 wakaba 1.174 return;
712     }
713 wakaba 1.136 }
714 wakaba 1.110
715     ($self->{line_prev}, $self->{column_prev})
716     = ($self->{line}, $self->{column});
717     $self->{column}++;
718 wakaba 1.1
719 wakaba 1.179 if ($self->{nc} == 0x000A) { # LF
720 wakaba 1.129
721 wakaba 1.110 $self->{line}++;
722     $self->{column} = 0;
723 wakaba 1.179 } elsif ($self->{nc} == 0x000D) { # CR
724 wakaba 1.129
725 wakaba 1.166 ## TODO: support for abort/streaming
726 wakaba 1.174 my $next = '';
727     if ($input->read ($next, 1) and $next ne "\x0A") {
728 wakaba 1.179 $self->{next_nc} = $next;
729 wakaba 1.132 }
730 wakaba 1.179 $self->{nc} = 0x000A; # LF # MUST
731 wakaba 1.110 $self->{line}++;
732     $self->{column} = 0;
733 wakaba 1.179 } elsif ($self->{nc} == 0x0000) { # NULL
734 wakaba 1.129
735 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'NULL');
736 wakaba 1.179 $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737 wakaba 1.1 }
738     };
739    
740 wakaba 1.168 $self->{read_until} = sub {
741     #my ($scalar, $specials_range, $offset) = @_;
742 wakaba 1.179 return 0 if defined $self->{next_nc};
743 wakaba 1.176
744 wakaba 1.178 my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745 wakaba 1.176 my $offset = $_[2] || 0;
746    
747     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748     pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749     if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750     substr ($_[0], $offset)
751     = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752     my $count = $+[0] - $-[0];
753     if ($count) {
754     $self->{column} += $count;
755     $self->{char_buffer_pos} += $count;
756     $self->{line_prev} = $self->{line};
757     $self->{column_prev} = $self->{column} - 1;
758 wakaba 1.179 $self->{nc} = -1;
759 wakaba 1.176 }
760     return $count;
761     } else {
762     return 0;
763     }
764     } else {
765     my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766     if ($count) {
767     $self->{column} += $count;
768     $self->{line_prev} = $self->{line};
769     $self->{column_prev} = $self->{column} - 1;
770 wakaba 1.179 $self->{nc} = -1;
771 wakaba 1.176 }
772     return $count;
773 wakaba 1.168 }
774     }; # $self->{read_until}
775 wakaba 1.167
776 wakaba 1.3 my $onerror = $_[2] || sub {
777     my (%opt) = @_;
778 wakaba 1.110 my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779     my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780     warn "Parse error ($opt{type}) at line $line column $column\n";
781 wakaba 1.3 };
782     $self->{parse_error} = sub {
783 wakaba 1.110 $onerror->(line => $self->{line}, column => $self->{column}, @_);
784 wakaba 1.1 };
785    
786 wakaba 1.178 my $char_onerror = sub {
787     my (undef, $type, %opt) = @_;
788     $self->{parse_error}->(level => $self->{level}->{must}, layer => 'encode',
789     line => $self->{line}, column => $self->{column} + 1,
790     %opt, type => $type);
791     }; # $char_onerror
792    
793     if ($_[3]) {
794     $input = $_[3]->($input);
795     $input->onerror ($char_onerror);
796     } else {
797     $input->onerror ($char_onerror) unless defined $input->onerror;
798     }
799    
800 wakaba 1.1 $self->_initialize_tokenizer;
801     $self->_initialize_tree_constructor;
802     $self->_construct_tree;
803     $self->_terminate_tree_constructor;
804    
805 wakaba 1.110 delete $self->{parse_error}; # remove loop
806    
807 wakaba 1.1 return $self->{document};
808 wakaba 1.132 } # parse_char_stream
809 wakaba 1.1
810     sub new ($) {
811     my $class = shift;
812 wakaba 1.131 my $self = bless {
813 wakaba 1.150 level => {must => 'm',
814 wakaba 1.155 should => 's',
815 wakaba 1.150 warn => 'w',
816     info => 'i',
817     uncertain => 'u'},
818 wakaba 1.131 }, $class;
819 wakaba 1.179 $self->{set_nc} = sub {
820     $self->{nc} = -1;
821 wakaba 1.1 };
822     $self->{parse_error} = sub {
823     #
824     };
825 wakaba 1.65 $self->{change_encoding} = sub {
826     # if ($_[0] is a supported encoding) {
827     # run "change the encoding" algorithm;
828     # throw Whatpm::HTML::RestartParser (charset => $new_encoding);
829     # }
830     };
831 wakaba 1.63 $self->{application_cache_selection} = sub {
832     #
833     };
834 wakaba 1.1 return $self;
835     } # new
836    
837 wakaba 1.41 sub CM_ENTITY () { 0b001 } # & markup in data
838     sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
839     sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
840    
841     sub PLAINTEXT_CONTENT_MODEL () { 0 }
842     sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
843     sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
844     sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846 wakaba 1.59 sub DATA_STATE () { 0 }
847 wakaba 1.164 #sub ENTITY_DATA_STATE () { 1 }
848 wakaba 1.59 sub TAG_OPEN_STATE () { 2 }
849     sub CLOSE_TAG_OPEN_STATE () { 3 }
850     sub TAG_NAME_STATE () { 4 }
851     sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
852     sub ATTRIBUTE_NAME_STATE () { 6 }
853     sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
854     sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
855     sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856     sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857     sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858 wakaba 1.164 #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859 wakaba 1.59 sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860     sub COMMENT_START_STATE () { 14 }
861     sub COMMENT_START_DASH_STATE () { 15 }
862     sub COMMENT_STATE () { 16 }
863     sub COMMENT_END_STATE () { 17 }
864     sub COMMENT_END_DASH_STATE () { 18 }
865     sub BOGUS_COMMENT_STATE () { 19 }
866     sub DOCTYPE_STATE () { 20 }
867     sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
868     sub DOCTYPE_NAME_STATE () { 22 }
869     sub AFTER_DOCTYPE_NAME_STATE () { 23 }
870     sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
871     sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
872     sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
873     sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
874     sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
875     sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
876     sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
877     sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878     sub BOGUS_DOCTYPE_STATE () { 32 }
879 wakaba 1.72 sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880 wakaba 1.122 sub SELF_CLOSING_START_TAG_STATE () { 34 }
881 wakaba 1.161 sub CDATA_SECTION_STATE () { 35 }
882 wakaba 1.160 sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883     sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884     sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885 wakaba 1.181 sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886 wakaba 1.161 sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887     sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888 wakaba 1.162 sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889     sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890 wakaba 1.165 ## NOTE: "Entity data state", "entity in attribute value state", and
891     ## "consume a character reference" algorithm are jointly implemented
892     ## using the following six states:
893     sub ENTITY_STATE () { 44 }
894     sub ENTITY_HASH_STATE () { 45 }
895     sub NCR_NUM_STATE () { 46 }
896     sub HEXREF_X_STATE () { 47 }
897     sub HEXREF_HEX_STATE () { 48 }
898     sub ENTITY_NAME_STATE () { 49 }
899 wakaba 1.181 sub PCDATA_STATE () { 50 } # "data state" in the spec
900 wakaba 1.59
901 wakaba 1.57 sub DOCTYPE_TOKEN () { 1 }
902     sub COMMENT_TOKEN () { 2 }
903     sub START_TAG_TOKEN () { 3 }
904     sub END_TAG_TOKEN () { 4 }
905     sub END_OF_FILE_TOKEN () { 5 }
906     sub CHARACTER_TOKEN () { 6 }
907    
908 wakaba 1.56 sub AFTER_HTML_IMS () { 0b100 }
909     sub HEAD_IMS () { 0b1000 }
910     sub BODY_IMS () { 0b10000 }
911 wakaba 1.58 sub BODY_TABLE_IMS () { 0b100000 }
912 wakaba 1.56 sub TABLE_IMS () { 0b1000000 }
913 wakaba 1.58 sub ROW_IMS () { 0b10000000 }
914 wakaba 1.56 sub BODY_AFTER_IMS () { 0b100000000 }
915     sub FRAME_IMS () { 0b1000000000 }
916 wakaba 1.101 sub SELECT_IMS () { 0b10000000000 }
917 wakaba 1.123 sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918     ## NOTE: "in foreign content" insertion mode is special; it is combined
919     ## with the secondary insertion mode. In this parser, they are stored
920     ## together in the bit-or'ed form.
921 wakaba 1.200 sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
922     ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
923     ## combined with the original insertion mode. In thie parser,
924     ## they are stored together in the bit-or'ed form.
925 wakaba 1.56
926 wakaba 1.85 ## NOTE: "initial" and "before html" insertion modes have no constants.
927 wakaba 1.84
928     ## NOTE: "after after body" insertion mode.
929 wakaba 1.56 sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
930 wakaba 1.84
931     ## NOTE: "after after frameset" insertion mode.
932 wakaba 1.56 sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
933 wakaba 1.84
934 wakaba 1.56 sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
935     sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
936     sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
937     sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
938     sub IN_BODY_IM () { BODY_IMS }
939 wakaba 1.58 sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
940     sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
941     sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
942     sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
943 wakaba 1.56 sub IN_TABLE_IM () { TABLE_IMS }
944     sub AFTER_BODY_IM () { BODY_AFTER_IMS }
945     sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
946     sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
947 wakaba 1.101 sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
948     sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
949 wakaba 1.56 sub IN_COLUMN_GROUP_IM () { 0b10 }
950    
951 wakaba 1.1 ## Implementations MUST act as if state machine in the spec
952    
953     sub _initialize_tokenizer ($) {
954     my $self = shift;
955 wakaba 1.59 $self->{state} = DATA_STATE; # MUST
956 wakaba 1.179 #$self->{s_kwd}; # state keyword - initialized when used
957 wakaba 1.165 #$self->{entity__value}; # initialized when used
958     #$self->{entity__match}; # initialized when used
959 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # be
960 wakaba 1.179 undef $self->{ct}; # current token
961     undef $self->{ca}; # current attribute
962     undef $self->{last_stag_name}; # last emitted start tag name
963 wakaba 1.165 #$self->{prev_state}; # initialized when used
964 wakaba 1.122 delete $self->{self_closing};
965 wakaba 1.175 $self->{char_buffer} = '';
966     $self->{char_buffer_pos} = 0;
967 wakaba 1.179 $self->{nc} = -1; # next input character
968     #$self->{next_nc}
969 wakaba 1.1
970 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
971     $self->{line_prev} = $self->{line};
972     $self->{column_prev} = $self->{column};
973     $self->{column}++;
974 wakaba 1.179 $self->{nc}
975 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
976     } else {
977 wakaba 1.179 $self->{set_nc}->($self);
978 wakaba 1.175 }
979 wakaba 1.1
980     $self->{token} = [];
981 wakaba 1.18 # $self->{escape}
982 wakaba 1.1 } # _initialize_tokenizer
983    
984     ## A token has:
985 wakaba 1.57 ## ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
986     ## CHARACTER_TOKEN, or END_OF_FILE_TOKEN
987     ## ->{name} (DOCTYPE_TOKEN)
988     ## ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
989 wakaba 1.179 ## ->{pubid} (DOCTYPE_TOKEN)
990     ## ->{sysid} (DOCTYPE_TOKEN)
991 wakaba 1.75 ## ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
992 wakaba 1.57 ## ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
993 wakaba 1.68 ## ->{name}
994     ## ->{value}
995     ## ->{has_reference} == 1 or 0
996 wakaba 1.57 ## ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
997 wakaba 1.122 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
998     ## |->{self_closing}| is used to save the value of |$self->{self_closing}|
999     ## while the token is pushed back to the stack.
1000    
1001 wakaba 1.1 ## Emitted token MUST immediately be handled by the tree construction state.
1002    
1003     ## Before each step, UA MAY check to see if either one of the scripts in
1004     ## "list of scripts that will execute as soon as possible" or the first
1005     ## script in the "list of scripts that will execute asynchronously",
1006     ## has completed loading. If one has, then it MUST be executed
1007     ## and removed from the list.
1008    
1009 wakaba 1.165 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1010     ## (This requirement was dropped from HTML5 spec, unfortunately.)
1011 wakaba 1.61
1012 wakaba 1.183 my $is_space = {
1013     0x0009 => 1, # CHARACTER TABULATION (HT)
1014     0x000A => 1, # LINE FEED (LF)
1015     #0x000B => 0, # LINE TABULATION (VT)
1016     0x000C => 1, # FORM FEED (FF)
1017     #0x000D => 1, # CARRIAGE RETURN (CR)
1018     0x0020 => 1, # SPACE (SP)
1019     };
1020    
1021 wakaba 1.1 sub _get_next_token ($) {
1022     my $self = shift;
1023 wakaba 1.122
1024     if ($self->{self_closing}) {
1025 wakaba 1.179 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc', token => $self->{ct});
1026 wakaba 1.122 ## NOTE: The |self_closing| flag is only set by start tag token.
1027     ## In addition, when a start tag token is emitted, it is always set to
1028 wakaba 1.179 ## |ct|.
1029 wakaba 1.122 delete $self->{self_closing};
1030     }
1031    
1032 wakaba 1.1 if (@{$self->{token}}) {
1033 wakaba 1.122 $self->{self_closing} = $self->{token}->[0]->{self_closing};
1034 wakaba 1.1 return shift @{$self->{token}};
1035     }
1036    
1037     A: {
1038 wakaba 1.181 if ($self->{state} == PCDATA_STATE) {
1039     ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1040    
1041     if ($self->{nc} == 0x0026) { # &
1042 wakaba 1.182
1043 wakaba 1.181 ## NOTE: In the spec, the tokenizer is switched to the
1044     ## "entity data state". In this implementation, the tokenizer
1045     ## is switched to the |ENTITY_STATE|, which is an implementation
1046     ## of the "consume a character reference" algorithm.
1047     $self->{entity_add} = -1;
1048     $self->{prev_state} = DATA_STATE;
1049     $self->{state} = ENTITY_STATE;
1050    
1051     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1052     $self->{line_prev} = $self->{line};
1053     $self->{column_prev} = $self->{column};
1054     $self->{column}++;
1055     $self->{nc}
1056     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1057     } else {
1058     $self->{set_nc}->($self);
1059     }
1060    
1061     redo A;
1062     } elsif ($self->{nc} == 0x003C) { # <
1063    
1064     $self->{state} = TAG_OPEN_STATE;
1065    
1066     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1067     $self->{line_prev} = $self->{line};
1068     $self->{column_prev} = $self->{column};
1069     $self->{column}++;
1070     $self->{nc}
1071     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1072     } else {
1073     $self->{set_nc}->($self);
1074     }
1075    
1076     redo A;
1077     } elsif ($self->{nc} == -1) {
1078    
1079     return ({type => END_OF_FILE_TOKEN,
1080     line => $self->{line}, column => $self->{column}});
1081     last A; ## TODO: ok?
1082     } else {
1083    
1084     #
1085     }
1086    
1087     # Anything else
1088     my $token = {type => CHARACTER_TOKEN,
1089     data => chr $self->{nc},
1090     line => $self->{line}, column => $self->{column},
1091     };
1092     $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1093    
1094     ## Stay in the state.
1095    
1096     if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1097     $self->{line_prev} = $self->{line};
1098     $self->{column_prev} = $self->{column};
1099     $self->{column}++;
1100     $self->{nc}
1101     = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1102     } else {
1103     $self->{set_nc}->($self);
1104     }
1105    
1106     return ($token);
1107     redo A;
1108     } elsif ($self->{state} == DATA_STATE) {
1109     $self->{s_kwd} = '' unless defined $self->{s_kwd};
1110 wakaba 1.179 if ($self->{nc} == 0x0026) { # &
1111 wakaba 1.181 $self->{s_kwd} = '';
1112 wakaba 1.72 if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1113     not $self->{escape}) {
1114 wakaba 1.77
1115 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
1116     ## "entity data state". In this implementation, the tokenizer
1117     ## is switched to the |ENTITY_STATE|, which is an implementation
1118     ## of the "consume a character reference" algorithm.
1119 wakaba 1.179 $self->{entity_add} = -1;
1120 wakaba 1.165 $self->{prev_state} = DATA_STATE;
1121 wakaba 1.163 $self->{state} = ENTITY_STATE;
1122 wakaba 1.1
1123 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1124     $self->{line_prev} = $self->{line};
1125     $self->{column_prev} = $self->{column};
1126     $self->{column}++;
1127 wakaba 1.179 $self->{nc}
1128 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1129     } else {
1130 wakaba 1.179 $self->{set_nc}->($self);
1131 wakaba 1.175 }
1132 wakaba 1.1
1133     redo A;
1134     } else {
1135 wakaba 1.77
1136 wakaba 1.1 #
1137     }
1138 wakaba 1.179 } elsif ($self->{nc} == 0x002D) { # -
1139 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1140 wakaba 1.181 $self->{s_kwd} .= '-';
1141    
1142 wakaba 1.180 if ($self->{s_kwd} eq '<!--') {
1143    
1144     $self->{escape} = 1; # unless $self->{escape};
1145     $self->{s_kwd} = '--';
1146     #
1147     } elsif ($self->{s_kwd} eq '---') {
1148    
1149     $self->{s_kwd} = '--';
1150     #
1151 wakaba 1.77 } else {
1152    
1153 wakaba 1.180 #
1154 wakaba 1.13 }
1155     }
1156    
1157     #
1158 wakaba 1.180 } elsif ($self->{nc} == 0x0021) { # !
1159 wakaba 1.181 if (length $self->{s_kwd}) {
1160 wakaba 1.180
1161     $self->{s_kwd} .= '!';
1162     #
1163     } else {
1164    
1165 wakaba 1.181 #$self->{s_kwd} = '';
1166 wakaba 1.180 #
1167     }
1168     #
1169 wakaba 1.179 } elsif ($self->{nc} == 0x003C) { # <
1170 wakaba 1.41 if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1171     (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1172 wakaba 1.13 not $self->{escape})) {
1173 wakaba 1.77
1174 wakaba 1.59 $self->{state} = TAG_OPEN_STATE;
1175 wakaba 1.1
1176 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1177     $self->{line_prev} = $self->{line};
1178     $self->{column_prev} = $self->{column};
1179     $self->{column}++;
1180 wakaba 1.179 $self->{nc}
1181 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1182     } else {
1183 wakaba 1.179 $self->{set_nc}->($self);
1184 wakaba 1.175 }
1185 wakaba 1.1
1186     redo A;
1187     } else {
1188 wakaba 1.77
1189 wakaba 1.181 $self->{s_kwd} = '';
1190 wakaba 1.1 #
1191     }
1192 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1193 wakaba 1.13 if ($self->{escape} and
1194 wakaba 1.41 ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1195 wakaba 1.181 if ($self->{s_kwd} eq '--') {
1196 wakaba 1.77
1197 wakaba 1.13 delete $self->{escape};
1198 wakaba 1.77 } else {
1199    
1200 wakaba 1.13 }
1201 wakaba 1.77 } else {
1202    
1203 wakaba 1.13 }
1204    
1205 wakaba 1.181 $self->{s_kwd} = '';
1206 wakaba 1.13 #
1207 wakaba 1.179 } elsif ($self->{nc} == -1) {
1208 wakaba 1.77
1209 wakaba 1.181 $self->{s_kwd} = '';
1210 wakaba 1.110 return ({type => END_OF_FILE_TOKEN,
1211     line => $self->{line}, column => $self->{column}});
1212 wakaba 1.1 last A; ## TODO: ok?
1213 wakaba 1.77 } else {
1214    
1215 wakaba 1.181 $self->{s_kwd} = '';
1216 wakaba 1.180 #
1217 wakaba 1.1 }
1218 wakaba 1.180
1219 wakaba 1.1 # Anything else
1220 wakaba 1.57 my $token = {type => CHARACTER_TOKEN,
1221 wakaba 1.179 data => chr $self->{nc},
1222 wakaba 1.118 line => $self->{line}, column => $self->{column},
1223 wakaba 1.116 };
1224 wakaba 1.180 if ($self->{read_until}->($token->{data}, q[-!<>&],
1225     length $token->{data})) {
1226 wakaba 1.181 $self->{s_kwd} = '';
1227 wakaba 1.180 }
1228 wakaba 1.167
1229 wakaba 1.181 ## Stay in the data state.
1230     if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1231 wakaba 1.182
1232 wakaba 1.181 $self->{state} = PCDATA_STATE;
1233     } else {
1234 wakaba 1.182
1235 wakaba 1.181 ## Stay in the state.
1236     }
1237 wakaba 1.1
1238 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1239     $self->{line_prev} = $self->{line};
1240     $self->{column_prev} = $self->{column};
1241     $self->{column}++;
1242 wakaba 1.179 $self->{nc}
1243 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1244     } else {
1245 wakaba 1.179 $self->{set_nc}->($self);
1246 wakaba 1.175 }
1247 wakaba 1.1
1248     return ($token);
1249     redo A;
1250 wakaba 1.59 } elsif ($self->{state} == TAG_OPEN_STATE) {
1251 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1252 wakaba 1.179 if ($self->{nc} == 0x002F) { # /
1253 wakaba 1.1
1254 wakaba 1.77
1255 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1256     $self->{line_prev} = $self->{line};
1257     $self->{column_prev} = $self->{column};
1258     $self->{column}++;
1259 wakaba 1.179 $self->{nc}
1260 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1261     } else {
1262 wakaba 1.179 $self->{set_nc}->($self);
1263 wakaba 1.175 }
1264 wakaba 1.1
1265 wakaba 1.59 $self->{state} = CLOSE_TAG_OPEN_STATE;
1266 wakaba 1.1 redo A;
1267 wakaba 1.180 } elsif ($self->{nc} == 0x0021) { # !
1268    
1269     $self->{s_kwd} = '<' unless $self->{escape};
1270     #
1271 wakaba 1.1 } else {
1272 wakaba 1.77
1273 wakaba 1.180 #
1274     }
1275 wakaba 1.1
1276 wakaba 1.180 ## reconsume
1277     $self->{state} = DATA_STATE;
1278     return ({type => CHARACTER_TOKEN, data => '<',
1279     line => $self->{line_prev},
1280     column => $self->{column_prev},
1281     });
1282     redo A;
1283 wakaba 1.41 } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1284 wakaba 1.179 if ($self->{nc} == 0x0021) { # !
1285 wakaba 1.77
1286 wakaba 1.59 $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1287 wakaba 1.1
1288 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1289     $self->{line_prev} = $self->{line};
1290     $self->{column_prev} = $self->{column};
1291     $self->{column}++;
1292 wakaba 1.179 $self->{nc}
1293 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1294     } else {
1295 wakaba 1.179 $self->{set_nc}->($self);
1296 wakaba 1.175 }
1297 wakaba 1.1
1298     redo A;
1299 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1300 wakaba 1.77
1301 wakaba 1.59 $self->{state} = CLOSE_TAG_OPEN_STATE;
1302 wakaba 1.1
1303 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1304     $self->{line_prev} = $self->{line};
1305     $self->{column_prev} = $self->{column};
1306     $self->{column}++;
1307 wakaba 1.179 $self->{nc}
1308 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1309     } else {
1310 wakaba 1.179 $self->{set_nc}->($self);
1311 wakaba 1.175 }
1312 wakaba 1.1
1313     redo A;
1314 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1315     $self->{nc} <= 0x005A) { # A..Z
1316 wakaba 1.77
1317 wakaba 1.179 $self->{ct}
1318 wakaba 1.57 = {type => START_TAG_TOKEN,
1319 wakaba 1.179 tag_name => chr ($self->{nc} + 0x0020),
1320 wakaba 1.110 line => $self->{line_prev},
1321     column => $self->{column_prev}};
1322 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1323 wakaba 1.1
1324 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1325     $self->{line_prev} = $self->{line};
1326     $self->{column_prev} = $self->{column};
1327     $self->{column}++;
1328 wakaba 1.179 $self->{nc}
1329 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1330     } else {
1331 wakaba 1.179 $self->{set_nc}->($self);
1332 wakaba 1.175 }
1333 wakaba 1.1
1334     redo A;
1335 wakaba 1.179 } elsif (0x0061 <= $self->{nc} and
1336     $self->{nc} <= 0x007A) { # a..z
1337 wakaba 1.77
1338 wakaba 1.179 $self->{ct} = {type => START_TAG_TOKEN,
1339     tag_name => chr ($self->{nc}),
1340 wakaba 1.110 line => $self->{line_prev},
1341     column => $self->{column_prev}};
1342 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1343 wakaba 1.1
1344 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1345     $self->{line_prev} = $self->{line};
1346     $self->{column_prev} = $self->{column};
1347     $self->{column}++;
1348 wakaba 1.179 $self->{nc}
1349 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1350     } else {
1351 wakaba 1.179 $self->{set_nc}->($self);
1352 wakaba 1.175 }
1353 wakaba 1.1
1354     redo A;
1355 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1356 wakaba 1.77
1357 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty start tag',
1358 wakaba 1.113 line => $self->{line_prev},
1359     column => $self->{column_prev});
1360 wakaba 1.59 $self->{state} = DATA_STATE;
1361 wakaba 1.1
1362 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1363     $self->{line_prev} = $self->{line};
1364     $self->{column_prev} = $self->{column};
1365     $self->{column}++;
1366 wakaba 1.179 $self->{nc}
1367 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1368     } else {
1369 wakaba 1.179 $self->{set_nc}->($self);
1370 wakaba 1.175 }
1371 wakaba 1.1
1372    
1373 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '<>',
1374 wakaba 1.118 line => $self->{line_prev},
1375     column => $self->{column_prev},
1376 wakaba 1.116 });
1377 wakaba 1.1
1378     redo A;
1379 wakaba 1.179 } elsif ($self->{nc} == 0x003F) { # ?
1380 wakaba 1.77
1381 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'pio',
1382 wakaba 1.113 line => $self->{line_prev},
1383     column => $self->{column_prev});
1384 wakaba 1.59 $self->{state} = BOGUS_COMMENT_STATE;
1385 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN, data => '',
1386 wakaba 1.118 line => $self->{line_prev},
1387     column => $self->{column_prev},
1388 wakaba 1.116 };
1389 wakaba 1.179 ## $self->{nc} is intentionally left as is
1390 wakaba 1.1 redo A;
1391     } else {
1392 wakaba 1.77
1393 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare stago',
1394 wakaba 1.133 line => $self->{line_prev},
1395     column => $self->{column_prev});
1396 wakaba 1.59 $self->{state} = DATA_STATE;
1397 wakaba 1.1 ## reconsume
1398    
1399 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '<',
1400 wakaba 1.118 line => $self->{line_prev},
1401     column => $self->{column_prev},
1402 wakaba 1.116 });
1403 wakaba 1.1
1404     redo A;
1405     }
1406     } else {
1407 wakaba 1.41 die "$0: $self->{content_model} in tag open";
1408 wakaba 1.1 }
1409 wakaba 1.59 } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1410 wakaba 1.160 ## NOTE: The "close tag open state" in the spec is implemented as
1411 wakaba 1.181 ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1412 wakaba 1.160
1413 wakaba 1.112 my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1414 wakaba 1.41 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1415 wakaba 1.179 if (defined $self->{last_stag_name}) {
1416 wakaba 1.181 $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1417 wakaba 1.179 $self->{s_kwd} = '';
1418 wakaba 1.160 ## Reconsume.
1419     redo A;
1420 wakaba 1.23 } else {
1421     ## No start tag token has ever been emitted
1422 wakaba 1.161 ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1423 wakaba 1.77
1424 wakaba 1.59 $self->{state} = DATA_STATE;
1425 wakaba 1.161 ## Reconsume.
1426 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '</',
1427 wakaba 1.118 line => $l, column => $c,
1428 wakaba 1.116 });
1429 wakaba 1.1 redo A;
1430     }
1431     }
1432 wakaba 1.160
1433 wakaba 1.179 if (0x0041 <= $self->{nc} and
1434     $self->{nc} <= 0x005A) { # A..Z
1435 wakaba 1.77
1436 wakaba 1.179 $self->{ct}
1437 wakaba 1.110 = {type => END_TAG_TOKEN,
1438 wakaba 1.179 tag_name => chr ($self->{nc} + 0x0020),
1439 wakaba 1.110 line => $l, column => $c};
1440 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1441 wakaba 1.1
1442 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1443     $self->{line_prev} = $self->{line};
1444     $self->{column_prev} = $self->{column};
1445     $self->{column}++;
1446 wakaba 1.179 $self->{nc}
1447 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1448     } else {
1449 wakaba 1.179 $self->{set_nc}->($self);
1450 wakaba 1.175 }
1451 wakaba 1.1
1452     redo A;
1453 wakaba 1.179 } elsif (0x0061 <= $self->{nc} and
1454     $self->{nc} <= 0x007A) { # a..z
1455 wakaba 1.77
1456 wakaba 1.179 $self->{ct} = {type => END_TAG_TOKEN,
1457     tag_name => chr ($self->{nc}),
1458 wakaba 1.110 line => $l, column => $c};
1459 wakaba 1.59 $self->{state} = TAG_NAME_STATE;
1460 wakaba 1.1
1461 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1462     $self->{line_prev} = $self->{line};
1463     $self->{column_prev} = $self->{column};
1464     $self->{column}++;
1465 wakaba 1.179 $self->{nc}
1466 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1467     } else {
1468 wakaba 1.179 $self->{set_nc}->($self);
1469 wakaba 1.175 }
1470 wakaba 1.1
1471     redo A;
1472 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1473 wakaba 1.77
1474 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty end tag',
1475 wakaba 1.113 line => $self->{line_prev}, ## "<" in "</>"
1476     column => $self->{column_prev} - 1);
1477 wakaba 1.59 $self->{state} = DATA_STATE;
1478 wakaba 1.1
1479 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1480     $self->{line_prev} = $self->{line};
1481     $self->{column_prev} = $self->{column};
1482     $self->{column}++;
1483 wakaba 1.179 $self->{nc}
1484 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1485     } else {
1486 wakaba 1.179 $self->{set_nc}->($self);
1487 wakaba 1.175 }
1488 wakaba 1.1
1489     redo A;
1490 wakaba 1.179 } elsif ($self->{nc} == -1) {
1491 wakaba 1.77
1492 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare etago');
1493 wakaba 1.59 $self->{state} = DATA_STATE;
1494 wakaba 1.1 # reconsume
1495    
1496 wakaba 1.110 return ({type => CHARACTER_TOKEN, data => '</',
1497 wakaba 1.118 line => $l, column => $c,
1498 wakaba 1.116 });
1499 wakaba 1.1
1500     redo A;
1501     } else {
1502 wakaba 1.77
1503 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus end tag');
1504 wakaba 1.59 $self->{state} = BOGUS_COMMENT_STATE;
1505 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN, data => '',
1506 wakaba 1.118 line => $self->{line_prev}, # "<" of "</"
1507     column => $self->{column_prev} - 1,
1508 wakaba 1.116 };
1509 wakaba 1.179 ## NOTE: $self->{nc} is intentionally left as is.
1510 wakaba 1.160 ## Although the "anything else" case of the spec not explicitly
1511     ## states that the next input character is to be reconsumed,
1512     ## it will be included to the |data| of the comment token
1513     ## generated from the bogus end tag, as defined in the
1514     ## "bogus comment state" entry.
1515     redo A;
1516     }
1517 wakaba 1.181 } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1518 wakaba 1.179 my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1519 wakaba 1.160 if (length $ch) {
1520     my $CH = $ch;
1521     $ch =~ tr/a-z/A-Z/;
1522 wakaba 1.179 my $nch = chr $self->{nc};
1523 wakaba 1.160 if ($nch eq $ch or $nch eq $CH) {
1524    
1525     ## Stay in the state.
1526 wakaba 1.179 $self->{s_kwd} .= $nch;
1527 wakaba 1.160
1528 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1529     $self->{line_prev} = $self->{line};
1530     $self->{column_prev} = $self->{column};
1531     $self->{column}++;
1532 wakaba 1.179 $self->{nc}
1533 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1534     } else {
1535 wakaba 1.179 $self->{set_nc}->($self);
1536 wakaba 1.175 }
1537 wakaba 1.160
1538     redo A;
1539     } else {
1540    
1541     $self->{state} = DATA_STATE;
1542     ## Reconsume.
1543     return ({type => CHARACTER_TOKEN,
1544 wakaba 1.179 data => '</' . $self->{s_kwd},
1545 wakaba 1.160 line => $self->{line_prev},
1546 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd},
1547 wakaba 1.160 });
1548     redo A;
1549     }
1550     } else { # after "<{tag-name}"
1551 wakaba 1.183 unless ($is_space->{$self->{nc}} or
1552     {
1553 wakaba 1.160 0x003E => 1, # >
1554     0x002F => 1, # /
1555     -1 => 1, # EOF
1556 wakaba 1.179 }->{$self->{nc}}) {
1557 wakaba 1.160
1558     ## Reconsume.
1559     $self->{state} = DATA_STATE;
1560     return ({type => CHARACTER_TOKEN,
1561 wakaba 1.179 data => '</' . $self->{s_kwd},
1562 wakaba 1.160 line => $self->{line_prev},
1563 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd},
1564 wakaba 1.160 });
1565     redo A;
1566     } else {
1567    
1568 wakaba 1.179 $self->{ct}
1569 wakaba 1.160 = {type => END_TAG_TOKEN,
1570 wakaba 1.179 tag_name => $self->{last_stag_name},
1571 wakaba 1.160 line => $self->{line_prev},
1572 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1573 wakaba 1.160 $self->{state} = TAG_NAME_STATE;
1574     ## Reconsume.
1575     redo A;
1576     }
1577 wakaba 1.1 }
1578 wakaba 1.59 } elsif ($self->{state} == TAG_NAME_STATE) {
1579 wakaba 1.183 if ($is_space->{$self->{nc}}) {
1580 wakaba 1.77
1581 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1582 wakaba 1.1
1583 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1584     $self->{line_prev} = $self->{line};
1585     $self->{column_prev} = $self->{column};
1586     $self->{column}++;
1587 wakaba 1.179 $self->{nc}
1588 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1589     } else {
1590 wakaba 1.179 $self->{set_nc}->($self);
1591 wakaba 1.175 }
1592 wakaba 1.1
1593     redo A;
1594 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1595     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1596 wakaba 1.77
1597 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1598     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1599 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1600 wakaba 1.179 #if ($self->{ct}->{attributes}) {
1601 wakaba 1.78 # ## NOTE: This should never be reached.
1602     # !!! cp (36);
1603     # !!! parse-error (type => 'end tag attribute');
1604     #} else {
1605 wakaba 1.77
1606 wakaba 1.78 #}
1607 wakaba 1.1 } else {
1608 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1609 wakaba 1.1 }
1610 wakaba 1.59 $self->{state} = DATA_STATE;
1611 wakaba 1.1
1612 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1613     $self->{line_prev} = $self->{line};
1614     $self->{column_prev} = $self->{column};
1615     $self->{column}++;
1616 wakaba 1.179 $self->{nc}
1617 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1618     } else {
1619 wakaba 1.179 $self->{set_nc}->($self);
1620 wakaba 1.175 }
1621 wakaba 1.1
1622    
1623 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1624 wakaba 1.1
1625     redo A;
1626 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1627     $self->{nc} <= 0x005A) { # A..Z
1628 wakaba 1.77
1629 wakaba 1.179 $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1630 wakaba 1.1 # start tag or end tag
1631     ## Stay in this state
1632    
1633 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1634     $self->{line_prev} = $self->{line};
1635     $self->{column_prev} = $self->{column};
1636     $self->{column}++;
1637 wakaba 1.179 $self->{nc}
1638 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1639     } else {
1640 wakaba 1.179 $self->{set_nc}->($self);
1641 wakaba 1.175 }
1642 wakaba 1.1
1643     redo A;
1644 wakaba 1.179 } elsif ($self->{nc} == -1) {
1645 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1646 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1647 wakaba 1.77
1648 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1649     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1650 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1651 wakaba 1.179 #if ($self->{ct}->{attributes}) {
1652 wakaba 1.78 # ## NOTE: This state should never be reached.
1653     # !!! cp (40);
1654     # !!! parse-error (type => 'end tag attribute');
1655     #} else {
1656 wakaba 1.77
1657 wakaba 1.78 #}
1658 wakaba 1.1 } else {
1659 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1660 wakaba 1.1 }
1661 wakaba 1.59 $self->{state} = DATA_STATE;
1662 wakaba 1.1 # reconsume
1663    
1664 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1665 wakaba 1.1
1666     redo A;
1667 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1668 wakaba 1.1
1669 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1670    
1671 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1672     $self->{line_prev} = $self->{line};
1673     $self->{column_prev} = $self->{column};
1674     $self->{column}++;
1675 wakaba 1.179 $self->{nc}
1676 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1677     } else {
1678 wakaba 1.179 $self->{set_nc}->($self);
1679 wakaba 1.175 }
1680 wakaba 1.1
1681     redo A;
1682     } else {
1683 wakaba 1.77
1684 wakaba 1.179 $self->{ct}->{tag_name} .= chr $self->{nc};
1685 wakaba 1.1 # start tag or end tag
1686     ## Stay in the state
1687    
1688 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1689     $self->{line_prev} = $self->{line};
1690     $self->{column_prev} = $self->{column};
1691     $self->{column}++;
1692 wakaba 1.179 $self->{nc}
1693 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1694     } else {
1695 wakaba 1.179 $self->{set_nc}->($self);
1696 wakaba 1.175 }
1697 wakaba 1.1
1698     redo A;
1699     }
1700 wakaba 1.59 } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1701 wakaba 1.183 if ($is_space->{$self->{nc}}) {
1702 wakaba 1.77
1703 wakaba 1.1 ## Stay in the state
1704    
1705 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1706     $self->{line_prev} = $self->{line};
1707     $self->{column_prev} = $self->{column};
1708     $self->{column}++;
1709 wakaba 1.179 $self->{nc}
1710 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1711     } else {
1712 wakaba 1.179 $self->{set_nc}->($self);
1713 wakaba 1.175 }
1714 wakaba 1.1
1715     redo A;
1716 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1717     if ($self->{ct}->{type} == START_TAG_TOKEN) {
1718 wakaba 1.77
1719 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1720     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1721 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1722 wakaba 1.179 if ($self->{ct}->{attributes}) {
1723 wakaba 1.77
1724 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1725 wakaba 1.77 } else {
1726    
1727 wakaba 1.1 }
1728     } else {
1729 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1730 wakaba 1.1 }
1731 wakaba 1.59 $self->{state} = DATA_STATE;
1732 wakaba 1.1
1733 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1734     $self->{line_prev} = $self->{line};
1735     $self->{column_prev} = $self->{column};
1736     $self->{column}++;
1737 wakaba 1.179 $self->{nc}
1738 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1739     } else {
1740 wakaba 1.179 $self->{set_nc}->($self);
1741 wakaba 1.175 }
1742 wakaba 1.1
1743    
1744 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1745 wakaba 1.1
1746     redo A;
1747 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1748     $self->{nc} <= 0x005A) { # A..Z
1749 wakaba 1.77
1750 wakaba 1.179 $self->{ca}
1751     = {name => chr ($self->{nc} + 0x0020),
1752 wakaba 1.117 value => '',
1753     line => $self->{line}, column => $self->{column}};
1754 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
1755 wakaba 1.1
1756 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1757     $self->{line_prev} = $self->{line};
1758     $self->{column_prev} = $self->{column};
1759     $self->{column}++;
1760 wakaba 1.179 $self->{nc}
1761 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1762     } else {
1763 wakaba 1.179 $self->{set_nc}->($self);
1764 wakaba 1.175 }
1765 wakaba 1.1
1766     redo A;
1767 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1768 wakaba 1.1
1769 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1770    
1771 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1772     $self->{line_prev} = $self->{line};
1773     $self->{column_prev} = $self->{column};
1774     $self->{column}++;
1775 wakaba 1.179 $self->{nc}
1776 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1777     } else {
1778 wakaba 1.179 $self->{set_nc}->($self);
1779 wakaba 1.175 }
1780 wakaba 1.1
1781     redo A;
1782 wakaba 1.179 } elsif ($self->{nc} == -1) {
1783 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1784 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1785 wakaba 1.77
1786 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1787     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1788 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1789 wakaba 1.179 if ($self->{ct}->{attributes}) {
1790 wakaba 1.77
1791 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1792 wakaba 1.77 } else {
1793    
1794 wakaba 1.1 }
1795     } else {
1796 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1797 wakaba 1.1 }
1798 wakaba 1.59 $self->{state} = DATA_STATE;
1799 wakaba 1.1 # reconsume
1800    
1801 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1802 wakaba 1.1
1803     redo A;
1804     } else {
1805 wakaba 1.72 if ({
1806     0x0022 => 1, # "
1807     0x0027 => 1, # '
1808     0x003D => 1, # =
1809 wakaba 1.179 }->{$self->{nc}}) {
1810 wakaba 1.77
1811 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
1812 wakaba 1.77 } else {
1813    
1814 wakaba 1.72 }
1815 wakaba 1.179 $self->{ca}
1816     = {name => chr ($self->{nc}),
1817 wakaba 1.117 value => '',
1818     line => $self->{line}, column => $self->{column}};
1819 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
1820 wakaba 1.1
1821 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1822     $self->{line_prev} = $self->{line};
1823     $self->{column_prev} = $self->{column};
1824     $self->{column}++;
1825 wakaba 1.179 $self->{nc}
1826 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1827     } else {
1828 wakaba 1.179 $self->{set_nc}->($self);
1829 wakaba 1.175 }
1830 wakaba 1.1
1831     redo A;
1832     }
1833 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1834 wakaba 1.1 my $before_leave = sub {
1835 wakaba 1.179 if (exists $self->{ct}->{attributes} # start tag or end tag
1836     ->{$self->{ca}->{name}}) { # MUST
1837 wakaba 1.77
1838 wakaba 1.179 $self->{parse_error}->(level => $self->{level}->{must}, type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1839     ## Discard $self->{ca} # MUST
1840 wakaba 1.1 } else {
1841 wakaba 1.77
1842 wakaba 1.179 $self->{ct}->{attributes}->{$self->{ca}->{name}}
1843     = $self->{ca};
1844 wakaba 1.1 }
1845     }; # $before_leave
1846    
1847 wakaba 1.183 if ($is_space->{$self->{nc}}) {
1848 wakaba 1.77
1849 wakaba 1.1 $before_leave->();
1850 wakaba 1.59 $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1851 wakaba 1.1
1852 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1853     $self->{line_prev} = $self->{line};
1854     $self->{column_prev} = $self->{column};
1855     $self->{column}++;
1856 wakaba 1.179 $self->{nc}
1857 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1858     } else {
1859 wakaba 1.179 $self->{set_nc}->($self);
1860 wakaba 1.175 }
1861 wakaba 1.1
1862     redo A;
1863 wakaba 1.179 } elsif ($self->{nc} == 0x003D) { # =
1864 wakaba 1.77
1865 wakaba 1.1 $before_leave->();
1866 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1867 wakaba 1.1
1868 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1869     $self->{line_prev} = $self->{line};
1870     $self->{column_prev} = $self->{column};
1871     $self->{column}++;
1872 wakaba 1.179 $self->{nc}
1873 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1874     } else {
1875 wakaba 1.179 $self->{set_nc}->($self);
1876 wakaba 1.175 }
1877 wakaba 1.1
1878     redo A;
1879 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
1880 wakaba 1.1 $before_leave->();
1881 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1882 wakaba 1.77
1883 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1884     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1885 wakaba 1.77
1886 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1887 wakaba 1.179 if ($self->{ct}->{attributes}) {
1888 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1889 wakaba 1.1 }
1890     } else {
1891 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1892 wakaba 1.1 }
1893 wakaba 1.59 $self->{state} = DATA_STATE;
1894 wakaba 1.1
1895 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1896     $self->{line_prev} = $self->{line};
1897     $self->{column_prev} = $self->{column};
1898     $self->{column}++;
1899 wakaba 1.179 $self->{nc}
1900 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1901     } else {
1902 wakaba 1.179 $self->{set_nc}->($self);
1903 wakaba 1.175 }
1904 wakaba 1.1
1905    
1906 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1907 wakaba 1.1
1908     redo A;
1909 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
1910     $self->{nc} <= 0x005A) { # A..Z
1911 wakaba 1.77
1912 wakaba 1.179 $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1913 wakaba 1.1 ## Stay in the state
1914    
1915 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1916     $self->{line_prev} = $self->{line};
1917     $self->{column_prev} = $self->{column};
1918     $self->{column}++;
1919 wakaba 1.179 $self->{nc}
1920 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1921     } else {
1922 wakaba 1.179 $self->{set_nc}->($self);
1923 wakaba 1.175 }
1924 wakaba 1.1
1925     redo A;
1926 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
1927 wakaba 1.122
1928 wakaba 1.1 $before_leave->();
1929 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
1930 wakaba 1.1
1931 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1932     $self->{line_prev} = $self->{line};
1933     $self->{column_prev} = $self->{column};
1934     $self->{column}++;
1935 wakaba 1.179 $self->{nc}
1936 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1937     } else {
1938 wakaba 1.179 $self->{set_nc}->($self);
1939 wakaba 1.175 }
1940 wakaba 1.1
1941     redo A;
1942 wakaba 1.179 } elsif ($self->{nc} == -1) {
1943 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
1944 wakaba 1.1 $before_leave->();
1945 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
1946 wakaba 1.77
1947 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
1948     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1949 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1950 wakaba 1.179 if ($self->{ct}->{attributes}) {
1951 wakaba 1.77
1952 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
1953 wakaba 1.77 } else {
1954 wakaba 1.78 ## NOTE: This state should never be reached.
1955 wakaba 1.77
1956 wakaba 1.1 }
1957     } else {
1958 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
1959 wakaba 1.1 }
1960 wakaba 1.59 $self->{state} = DATA_STATE;
1961 wakaba 1.1 # reconsume
1962    
1963 wakaba 1.179 return ($self->{ct}); # start tag or end tag
1964 wakaba 1.1
1965     redo A;
1966     } else {
1967 wakaba 1.179 if ($self->{nc} == 0x0022 or # "
1968     $self->{nc} == 0x0027) { # '
1969 wakaba 1.77
1970 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
1971 wakaba 1.77 } else {
1972    
1973 wakaba 1.72 }
1974 wakaba 1.179 $self->{ca}->{name} .= chr ($self->{nc});
1975 wakaba 1.1 ## Stay in the state
1976    
1977 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1978     $self->{line_prev} = $self->{line};
1979     $self->{column_prev} = $self->{column};
1980     $self->{column}++;
1981 wakaba 1.179 $self->{nc}
1982 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
1983     } else {
1984 wakaba 1.179 $self->{set_nc}->($self);
1985 wakaba 1.175 }
1986 wakaba 1.1
1987     redo A;
1988     }
1989 wakaba 1.59 } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1990 wakaba 1.183 if ($is_space->{$self->{nc}}) {
1991 wakaba 1.77
1992 wakaba 1.1 ## Stay in the state
1993    
1994 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
1995     $self->{line_prev} = $self->{line};
1996     $self->{column_prev} = $self->{column};
1997     $self->{column}++;
1998 wakaba 1.179 $self->{nc}
1999 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2000     } else {
2001 wakaba 1.179 $self->{set_nc}->($self);
2002 wakaba 1.175 }
2003 wakaba 1.1
2004     redo A;
2005 wakaba 1.179 } elsif ($self->{nc} == 0x003D) { # =
2006 wakaba 1.77
2007 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
2008 wakaba 1.1
2009 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2010     $self->{line_prev} = $self->{line};
2011     $self->{column_prev} = $self->{column};
2012     $self->{column}++;
2013 wakaba 1.179 $self->{nc}
2014 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2015     } else {
2016 wakaba 1.179 $self->{set_nc}->($self);
2017 wakaba 1.175 }
2018 wakaba 1.1
2019     redo A;
2020 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2021     if ($self->{ct}->{type} == START_TAG_TOKEN) {
2022 wakaba 1.77
2023 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2024     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2025 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2026 wakaba 1.179 if ($self->{ct}->{attributes}) {
2027 wakaba 1.77
2028 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2029 wakaba 1.77 } else {
2030 wakaba 1.78 ## NOTE: This state should never be reached.
2031 wakaba 1.77
2032 wakaba 1.1 }
2033     } else {
2034 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2035 wakaba 1.1 }
2036 wakaba 1.59 $self->{state} = DATA_STATE;
2037 wakaba 1.1
2038 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2039     $self->{line_prev} = $self->{line};
2040     $self->{column_prev} = $self->{column};
2041     $self->{column}++;
2042 wakaba 1.179 $self->{nc}
2043 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2044     } else {
2045 wakaba 1.179 $self->{set_nc}->($self);
2046 wakaba 1.175 }
2047 wakaba 1.1
2048    
2049 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2050 wakaba 1.1
2051     redo A;
2052 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
2053     $self->{nc} <= 0x005A) { # A..Z
2054 wakaba 1.77
2055 wakaba 1.179 $self->{ca}
2056     = {name => chr ($self->{nc} + 0x0020),
2057 wakaba 1.117 value => '',
2058     line => $self->{line}, column => $self->{column}};
2059 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
2060 wakaba 1.1
2061 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2062     $self->{line_prev} = $self->{line};
2063     $self->{column_prev} = $self->{column};
2064     $self->{column}++;
2065 wakaba 1.179 $self->{nc}
2066 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2067     } else {
2068 wakaba 1.179 $self->{set_nc}->($self);
2069 wakaba 1.175 }
2070 wakaba 1.1
2071     redo A;
2072 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
2073 wakaba 1.1
2074 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
2075    
2076 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2077     $self->{line_prev} = $self->{line};
2078     $self->{column_prev} = $self->{column};
2079     $self->{column}++;
2080 wakaba 1.179 $self->{nc}
2081 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2082     } else {
2083 wakaba 1.179 $self->{set_nc}->($self);
2084 wakaba 1.175 }
2085 wakaba 1.1
2086     redo A;
2087 wakaba 1.179 } elsif ($self->{nc} == -1) {
2088 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2089 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2090 wakaba 1.77
2091 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2092     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2093 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2094 wakaba 1.179 if ($self->{ct}->{attributes}) {
2095 wakaba 1.77
2096 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2097 wakaba 1.77 } else {
2098 wakaba 1.78 ## NOTE: This state should never be reached.
2099 wakaba 1.77
2100 wakaba 1.1 }
2101     } else {
2102 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2103 wakaba 1.1 }
2104 wakaba 1.59 $self->{state} = DATA_STATE;
2105 wakaba 1.1 # reconsume
2106    
2107 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2108 wakaba 1.1
2109     redo A;
2110     } else {
2111 wakaba 1.179 if ($self->{nc} == 0x0022 or # "
2112     $self->{nc} == 0x0027) { # '
2113 wakaba 1.153
2114     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute name');
2115     } else {
2116    
2117     }
2118 wakaba 1.179 $self->{ca}
2119     = {name => chr ($self->{nc}),
2120 wakaba 1.117 value => '',
2121     line => $self->{line}, column => $self->{column}};
2122 wakaba 1.59 $self->{state} = ATTRIBUTE_NAME_STATE;
2123 wakaba 1.1
2124 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2125     $self->{line_prev} = $self->{line};
2126     $self->{column_prev} = $self->{column};
2127     $self->{column}++;
2128 wakaba 1.179 $self->{nc}
2129 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2130     } else {
2131 wakaba 1.179 $self->{set_nc}->($self);
2132 wakaba 1.175 }
2133 wakaba 1.1
2134     redo A;
2135     }
2136 wakaba 1.59 } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
2137 wakaba 1.183 if ($is_space->{$self->{nc}}) {
2138 wakaba 1.77
2139 wakaba 1.1 ## Stay in the state
2140    
2141 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2142     $self->{line_prev} = $self->{line};
2143     $self->{column_prev} = $self->{column};
2144     $self->{column}++;
2145 wakaba 1.179 $self->{nc}
2146 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2147     } else {
2148 wakaba 1.179 $self->{set_nc}->($self);
2149 wakaba 1.175 }
2150 wakaba 1.1
2151     redo A;
2152 wakaba 1.179 } elsif ($self->{nc} == 0x0022) { # "
2153 wakaba 1.77
2154 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
2155 wakaba 1.1
2156 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2157     $self->{line_prev} = $self->{line};
2158     $self->{column_prev} = $self->{column};
2159     $self->{column}++;
2160 wakaba 1.179 $self->{nc}
2161 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2162     } else {
2163 wakaba 1.179 $self->{set_nc}->($self);
2164 wakaba 1.175 }
2165 wakaba 1.1
2166     redo A;
2167 wakaba 1.179 } elsif ($self->{nc} == 0x0026) { # &
2168 wakaba 1.77
2169 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
2170 wakaba 1.1 ## reconsume
2171     redo A;
2172 wakaba 1.179 } elsif ($self->{nc} == 0x0027) { # '
2173 wakaba 1.77
2174 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
2175 wakaba 1.1
2176 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2177     $self->{line_prev} = $self->{line};
2178     $self->{column_prev} = $self->{column};
2179     $self->{column}++;
2180 wakaba 1.179 $self->{nc}
2181 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2182     } else {
2183 wakaba 1.179 $self->{set_nc}->($self);
2184 wakaba 1.175 }
2185 wakaba 1.1
2186     redo A;
2187 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2188 wakaba 1.153 $self->{parse_error}->(level => $self->{level}->{must}, type => 'empty unquoted attribute value');
2189 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2190 wakaba 1.77
2191 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2192     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2193 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2194 wakaba 1.179 if ($self->{ct}->{attributes}) {
2195 wakaba 1.77
2196 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2197 wakaba 1.77 } else {
2198 wakaba 1.78 ## NOTE: This state should never be reached.
2199 wakaba 1.77
2200 wakaba 1.1 }
2201     } else {
2202 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2203 wakaba 1.1 }
2204 wakaba 1.59 $self->{state} = DATA_STATE;
2205 wakaba 1.1
2206 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2207     $self->{line_prev} = $self->{line};
2208     $self->{column_prev} = $self->{column};
2209     $self->{column}++;
2210 wakaba 1.179 $self->{nc}
2211 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2212     } else {
2213 wakaba 1.179 $self->{set_nc}->($self);
2214 wakaba 1.175 }
2215 wakaba 1.1
2216    
2217 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2218 wakaba 1.1
2219     redo A;
2220 wakaba 1.179 } elsif ($self->{nc} == -1) {
2221 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2222 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2223 wakaba 1.77
2224 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2225     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2226 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2227 wakaba 1.179 if ($self->{ct}->{attributes}) {
2228 wakaba 1.77
2229 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2230 wakaba 1.77 } else {
2231 wakaba 1.78 ## NOTE: This state should never be reached.
2232 wakaba 1.77
2233 wakaba 1.1 }
2234     } else {
2235 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2236 wakaba 1.1 }
2237 wakaba 1.59 $self->{state} = DATA_STATE;
2238 wakaba 1.1 ## reconsume
2239    
2240 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2241 wakaba 1.1
2242     redo A;
2243     } else {
2244 wakaba 1.179 if ($self->{nc} == 0x003D) { # =
2245 wakaba 1.77
2246 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute value');
2247 wakaba 1.77 } else {
2248    
2249 wakaba 1.72 }
2250 wakaba 1.179 $self->{ca}->{value} .= chr ($self->{nc});
2251 wakaba 1.59 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
2252 wakaba 1.1
2253 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2254     $self->{line_prev} = $self->{line};
2255     $self->{column_prev} = $self->{column};
2256     $self->{column}++;
2257 wakaba 1.179 $self->{nc}
2258 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2259     } else {
2260 wakaba 1.179 $self->{set_nc}->($self);
2261 wakaba 1.175 }
2262 wakaba 1.1
2263     redo A;
2264     }
2265 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
2266 wakaba 1.179 if ($self->{nc} == 0x0022) { # "
2267 wakaba 1.77
2268 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
2269 wakaba 1.1
2270 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2271     $self->{line_prev} = $self->{line};
2272     $self->{column_prev} = $self->{column};
2273     $self->{column}++;
2274 wakaba 1.179 $self->{nc}
2275 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2276     } else {
2277 wakaba 1.179 $self->{set_nc}->($self);
2278 wakaba 1.175 }
2279 wakaba 1.1
2280     redo A;
2281 wakaba 1.179 } elsif ($self->{nc} == 0x0026) { # &
2282 wakaba 1.77
2283 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2284     ## "entity in attribute value state". In this implementation, the
2285     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2286     ## implementation of the "consume a character reference" algorithm.
2287 wakaba 1.165 $self->{prev_state} = $self->{state};
2288 wakaba 1.179 $self->{entity_add} = 0x0022; # "
2289 wakaba 1.163 $self->{state} = ENTITY_STATE;
2290 wakaba 1.1
2291 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2292     $self->{line_prev} = $self->{line};
2293     $self->{column_prev} = $self->{column};
2294     $self->{column}++;
2295 wakaba 1.179 $self->{nc}
2296 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2297     } else {
2298 wakaba 1.179 $self->{set_nc}->($self);
2299 wakaba 1.175 }
2300 wakaba 1.1
2301     redo A;
2302 wakaba 1.179 } elsif ($self->{nc} == -1) {
2303 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed attribute value');
2304 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2305 wakaba 1.77
2306 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2307     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2308 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2309 wakaba 1.179 if ($self->{ct}->{attributes}) {
2310 wakaba 1.77
2311 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2312 wakaba 1.77 } else {
2313 wakaba 1.78 ## NOTE: This state should never be reached.
2314 wakaba 1.77
2315 wakaba 1.1 }
2316     } else {
2317 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2318 wakaba 1.1 }
2319 wakaba 1.59 $self->{state} = DATA_STATE;
2320 wakaba 1.1 ## reconsume
2321    
2322 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2323 wakaba 1.1
2324     redo A;
2325     } else {
2326 wakaba 1.77
2327 wakaba 1.179 $self->{ca}->{value} .= chr ($self->{nc});
2328     $self->{read_until}->($self->{ca}->{value},
2329 wakaba 1.169 q["&],
2330 wakaba 1.179 length $self->{ca}->{value});
2331 wakaba 1.169
2332 wakaba 1.1 ## Stay in the state
2333    
2334 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2335     $self->{line_prev} = $self->{line};
2336     $self->{column_prev} = $self->{column};
2337     $self->{column}++;
2338 wakaba 1.179 $self->{nc}
2339 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2340     } else {
2341 wakaba 1.179 $self->{set_nc}->($self);
2342 wakaba 1.175 }
2343 wakaba 1.1
2344     redo A;
2345     }
2346 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
2347 wakaba 1.179 if ($self->{nc} == 0x0027) { # '
2348 wakaba 1.77
2349 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
2350 wakaba 1.1
2351 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2352     $self->{line_prev} = $self->{line};
2353     $self->{column_prev} = $self->{column};
2354     $self->{column}++;
2355 wakaba 1.179 $self->{nc}
2356 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2357     } else {
2358 wakaba 1.179 $self->{set_nc}->($self);
2359 wakaba 1.175 }
2360 wakaba 1.1
2361     redo A;
2362 wakaba 1.179 } elsif ($self->{nc} == 0x0026) { # &
2363 wakaba 1.77
2364 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2365     ## "entity in attribute value state". In this implementation, the
2366     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2367     ## implementation of the "consume a character reference" algorithm.
2368 wakaba 1.179 $self->{entity_add} = 0x0027; # '
2369 wakaba 1.165 $self->{prev_state} = $self->{state};
2370 wakaba 1.163 $self->{state} = ENTITY_STATE;
2371 wakaba 1.1
2372 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2373     $self->{line_prev} = $self->{line};
2374     $self->{column_prev} = $self->{column};
2375     $self->{column}++;
2376 wakaba 1.179 $self->{nc}
2377 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2378     } else {
2379 wakaba 1.179 $self->{set_nc}->($self);
2380 wakaba 1.175 }
2381 wakaba 1.1
2382     redo A;
2383 wakaba 1.179 } elsif ($self->{nc} == -1) {
2384 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed attribute value');
2385 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2386 wakaba 1.77
2387 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2388     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2389 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2390 wakaba 1.179 if ($self->{ct}->{attributes}) {
2391 wakaba 1.77
2392 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2393 wakaba 1.77 } else {
2394 wakaba 1.78 ## NOTE: This state should never be reached.
2395 wakaba 1.77
2396 wakaba 1.1 }
2397     } else {
2398 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2399 wakaba 1.1 }
2400 wakaba 1.59 $self->{state} = DATA_STATE;
2401 wakaba 1.1 ## reconsume
2402    
2403 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2404 wakaba 1.1
2405     redo A;
2406     } else {
2407 wakaba 1.77
2408 wakaba 1.179 $self->{ca}->{value} .= chr ($self->{nc});
2409     $self->{read_until}->($self->{ca}->{value},
2410 wakaba 1.169 q['&],
2411 wakaba 1.179 length $self->{ca}->{value});
2412 wakaba 1.169
2413 wakaba 1.1 ## Stay in the state
2414    
2415 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2416     $self->{line_prev} = $self->{line};
2417     $self->{column_prev} = $self->{column};
2418     $self->{column}++;
2419 wakaba 1.179 $self->{nc}
2420 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2421     } else {
2422 wakaba 1.179 $self->{set_nc}->($self);
2423 wakaba 1.175 }
2424 wakaba 1.1
2425     redo A;
2426     }
2427 wakaba 1.59 } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
2428 wakaba 1.183 if ($is_space->{$self->{nc}}) {
2429 wakaba 1.77
2430 wakaba 1.59 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2431 wakaba 1.1
2432 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2433     $self->{line_prev} = $self->{line};
2434     $self->{column_prev} = $self->{column};
2435     $self->{column}++;
2436 wakaba 1.179 $self->{nc}
2437 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2438     } else {
2439 wakaba 1.179 $self->{set_nc}->($self);
2440 wakaba 1.175 }
2441 wakaba 1.1
2442     redo A;
2443 wakaba 1.179 } elsif ($self->{nc} == 0x0026) { # &
2444 wakaba 1.77
2445 wakaba 1.163 ## NOTE: In the spec, the tokenizer is switched to the
2446     ## "entity in attribute value state". In this implementation, the
2447     ## tokenizer is switched to the |ENTITY_STATE|, which is an
2448     ## implementation of the "consume a character reference" algorithm.
2449 wakaba 1.179 $self->{entity_add} = -1;
2450 wakaba 1.165 $self->{prev_state} = $self->{state};
2451 wakaba 1.163 $self->{state} = ENTITY_STATE;
2452 wakaba 1.1
2453 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2454     $self->{line_prev} = $self->{line};
2455     $self->{column_prev} = $self->{column};
2456     $self->{column}++;
2457 wakaba 1.179 $self->{nc}
2458 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2459     } else {
2460 wakaba 1.179 $self->{set_nc}->($self);
2461 wakaba 1.175 }
2462 wakaba 1.1
2463     redo A;
2464 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2465     if ($self->{ct}->{type} == START_TAG_TOKEN) {
2466 wakaba 1.77
2467 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2468     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2469 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2470 wakaba 1.179 if ($self->{ct}->{attributes}) {
2471 wakaba 1.77
2472 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2473 wakaba 1.77 } else {
2474 wakaba 1.78 ## NOTE: This state should never be reached.
2475 wakaba 1.77
2476 wakaba 1.1 }
2477     } else {
2478 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2479 wakaba 1.1 }
2480 wakaba 1.59 $self->{state} = DATA_STATE;
2481 wakaba 1.1
2482 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2483     $self->{line_prev} = $self->{line};
2484     $self->{column_prev} = $self->{column};
2485     $self->{column}++;
2486 wakaba 1.179 $self->{nc}
2487 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2488     } else {
2489 wakaba 1.179 $self->{set_nc}->($self);
2490 wakaba 1.175 }
2491 wakaba 1.1
2492    
2493 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2494 wakaba 1.1
2495     redo A;
2496 wakaba 1.179 } elsif ($self->{nc} == -1) {
2497 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2498 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2499 wakaba 1.77
2500 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2501     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2502 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2503 wakaba 1.179 if ($self->{ct}->{attributes}) {
2504 wakaba 1.77
2505 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2506 wakaba 1.77 } else {
2507 wakaba 1.78 ## NOTE: This state should never be reached.
2508 wakaba 1.77
2509 wakaba 1.1 }
2510     } else {
2511 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2512 wakaba 1.1 }
2513 wakaba 1.59 $self->{state} = DATA_STATE;
2514 wakaba 1.1 ## reconsume
2515    
2516 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2517 wakaba 1.1
2518     redo A;
2519     } else {
2520 wakaba 1.72 if ({
2521     0x0022 => 1, # "
2522     0x0027 => 1, # '
2523     0x003D => 1, # =
2524 wakaba 1.179 }->{$self->{nc}}) {
2525 wakaba 1.77
2526 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad attribute value');
2527 wakaba 1.77 } else {
2528    
2529 wakaba 1.72 }
2530 wakaba 1.179 $self->{ca}->{value} .= chr ($self->{nc});
2531     $self->{read_until}->($self->{ca}->{value},
2532 wakaba 1.169 q["'=& >],
2533 wakaba 1.179 length $self->{ca}->{value});
2534 wakaba 1.169
2535 wakaba 1.1 ## Stay in the state
2536    
2537 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2538     $self->{line_prev} = $self->{line};
2539     $self->{column_prev} = $self->{column};
2540     $self->{column}++;
2541 wakaba 1.179 $self->{nc}
2542 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2543     } else {
2544 wakaba 1.179 $self->{set_nc}->($self);
2545 wakaba 1.175 }
2546 wakaba 1.1
2547     redo A;
2548     }
2549 wakaba 1.72 } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2550 wakaba 1.183 if ($is_space->{$self->{nc}}) {
2551 wakaba 1.77
2552 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2553    
2554 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2555     $self->{line_prev} = $self->{line};
2556     $self->{column_prev} = $self->{column};
2557     $self->{column}++;
2558 wakaba 1.179 $self->{nc}
2559 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2560     } else {
2561 wakaba 1.179 $self->{set_nc}->($self);
2562 wakaba 1.175 }
2563 wakaba 1.72
2564     redo A;
2565 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2566     if ($self->{ct}->{type} == START_TAG_TOKEN) {
2567 wakaba 1.77
2568 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2569     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2570 wakaba 1.72 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2571 wakaba 1.179 if ($self->{ct}->{attributes}) {
2572 wakaba 1.77
2573 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2574 wakaba 1.77 } else {
2575 wakaba 1.78 ## NOTE: This state should never be reached.
2576 wakaba 1.77
2577 wakaba 1.72 }
2578     } else {
2579 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2580 wakaba 1.72 }
2581     $self->{state} = DATA_STATE;
2582    
2583 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2584     $self->{line_prev} = $self->{line};
2585     $self->{column_prev} = $self->{column};
2586     $self->{column}++;
2587 wakaba 1.179 $self->{nc}
2588 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2589     } else {
2590 wakaba 1.179 $self->{set_nc}->($self);
2591 wakaba 1.175 }
2592 wakaba 1.72
2593    
2594 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2595 wakaba 1.72
2596     redo A;
2597 wakaba 1.179 } elsif ($self->{nc} == 0x002F) { # /
2598 wakaba 1.72
2599 wakaba 1.122 $self->{state} = SELF_CLOSING_START_TAG_STATE;
2600    
2601 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2602     $self->{line_prev} = $self->{line};
2603     $self->{column_prev} = $self->{column};
2604     $self->{column}++;
2605 wakaba 1.179 $self->{nc}
2606 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2607     } else {
2608 wakaba 1.179 $self->{set_nc}->($self);
2609 wakaba 1.175 }
2610 wakaba 1.72
2611 wakaba 1.122 redo A;
2612 wakaba 1.179 } elsif ($self->{nc} == -1) {
2613 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2614 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2615 wakaba 1.138
2616 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2617     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2618     if ($self->{ct}->{attributes}) {
2619 wakaba 1.138
2620 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2621 wakaba 1.138 } else {
2622     ## NOTE: This state should never be reached.
2623    
2624     }
2625     } else {
2626 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2627 wakaba 1.138 }
2628     $self->{state} = DATA_STATE;
2629     ## Reconsume.
2630 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2631 wakaba 1.138 redo A;
2632 wakaba 1.122 } else {
2633    
2634 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no space between attributes');
2635 wakaba 1.122 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2636     ## reconsume
2637     redo A;
2638     }
2639     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2640 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
2641     if ($self->{ct}->{type} == END_TAG_TOKEN) {
2642 wakaba 1.77
2643 wakaba 1.179 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc', token => $self->{ct});
2644 wakaba 1.122 ## TODO: Different type than slash in start tag
2645     $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2646 wakaba 1.179 if ($self->{ct}->{attributes}) {
2647 wakaba 1.122
2648 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2649 wakaba 1.122 } else {
2650    
2651     }
2652     ## TODO: Test |<title></title/>|
2653 wakaba 1.72 } else {
2654 wakaba 1.77
2655 wakaba 1.122 $self->{self_closing} = 1;
2656 wakaba 1.72 }
2657 wakaba 1.122
2658     $self->{state} = DATA_STATE;
2659    
2660 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2661     $self->{line_prev} = $self->{line};
2662     $self->{column_prev} = $self->{column};
2663     $self->{column}++;
2664 wakaba 1.179 $self->{nc}
2665 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2666     } else {
2667 wakaba 1.179 $self->{set_nc}->($self);
2668 wakaba 1.175 }
2669 wakaba 1.122
2670    
2671 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2672 wakaba 1.122
2673 wakaba 1.72 redo A;
2674 wakaba 1.179 } elsif ($self->{nc} == -1) {
2675 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed tag');
2676 wakaba 1.179 if ($self->{ct}->{type} == START_TAG_TOKEN) {
2677 wakaba 1.138
2678 wakaba 1.179 $self->{last_stag_name} = $self->{ct}->{tag_name};
2679     } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2680     if ($self->{ct}->{attributes}) {
2681 wakaba 1.138
2682 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'end tag attribute');
2683 wakaba 1.138 } else {
2684     ## NOTE: This state should never be reached.
2685    
2686     }
2687     } else {
2688 wakaba 1.179 die "$0: $self->{ct}->{type}: Unknown token type";
2689 wakaba 1.138 }
2690     $self->{state} = DATA_STATE;
2691     ## Reconsume.
2692 wakaba 1.179 return ($self->{ct}); # start tag or end tag
2693 wakaba 1.138 redo A;
2694 wakaba 1.72 } else {
2695 wakaba 1.77
2696 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'nestc');
2697 wakaba 1.122 ## TODO: This error type is wrong.
2698 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2699 wakaba 1.122 ## Reconsume.
2700 wakaba 1.72 redo A;
2701     }
2702 wakaba 1.59 } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2703 wakaba 1.1 ## (only happen if PCDATA state)
2704 wakaba 1.163
2705     ## NOTE: Unlike spec's "bogus comment state", this implementation
2706     ## consumes characters one-by-one basis.
2707 wakaba 1.1
2708 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
2709 wakaba 1.163
2710     $self->{state} = DATA_STATE;
2711    
2712 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2713     $self->{line_prev} = $self->{line};
2714     $self->{column_prev} = $self->{column};
2715     $self->{column}++;
2716 wakaba 1.179 $self->{nc}
2717 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2718     } else {
2719 wakaba 1.179 $self->{set_nc}->($self);
2720 wakaba 1.175 }
2721 wakaba 1.1
2722    
2723 wakaba 1.179 return ($self->{ct}); # comment
2724 wakaba 1.163 redo A;
2725 wakaba 1.179 } elsif ($self->{nc} == -1) {
2726 wakaba 1.163
2727     $self->{state} = DATA_STATE;
2728     ## reconsume
2729 wakaba 1.1
2730 wakaba 1.179 return ($self->{ct}); # comment
2731 wakaba 1.163 redo A;
2732     } else {
2733    
2734 wakaba 1.179 $self->{ct}->{data} .= chr ($self->{nc}); # comment
2735     $self->{read_until}->($self->{ct}->{data},
2736 wakaba 1.169 q[>],
2737 wakaba 1.179 length $self->{ct}->{data});
2738 wakaba 1.169
2739 wakaba 1.163 ## Stay in the state.
2740    
2741 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2742     $self->{line_prev} = $self->{line};
2743     $self->{column_prev} = $self->{column};
2744     $self->{column}++;
2745 wakaba 1.179 $self->{nc}
2746 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2747     } else {
2748 wakaba 1.179 $self->{set_nc}->($self);
2749 wakaba 1.175 }
2750 wakaba 1.1
2751 wakaba 1.163 redo A;
2752     }
2753 wakaba 1.59 } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2754 wakaba 1.1 ## (only happen if PCDATA state)
2755    
2756 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
2757 wakaba 1.1
2758 wakaba 1.159 $self->{state} = MD_HYPHEN_STATE;
2759    
2760 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2761     $self->{line_prev} = $self->{line};
2762     $self->{column_prev} = $self->{column};
2763     $self->{column}++;
2764 wakaba 1.179 $self->{nc}
2765 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2766     } else {
2767 wakaba 1.179 $self->{set_nc}->($self);
2768 wakaba 1.175 }
2769 wakaba 1.1
2770 wakaba 1.159 redo A;
2771 wakaba 1.179 } elsif ($self->{nc} == 0x0044 or # D
2772     $self->{nc} == 0x0064) { # d
2773 wakaba 1.159 ## ASCII case-insensitive.
2774    
2775     $self->{state} = MD_DOCTYPE_STATE;
2776 wakaba 1.179 $self->{s_kwd} = chr $self->{nc};
2777 wakaba 1.1
2778 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2779     $self->{line_prev} = $self->{line};
2780     $self->{column_prev} = $self->{column};
2781     $self->{column}++;
2782 wakaba 1.179 $self->{nc}
2783 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2784     } else {
2785 wakaba 1.179 $self->{set_nc}->($self);
2786 wakaba 1.175 }
2787 wakaba 1.1
2788 wakaba 1.159 redo A;
2789     } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2790     $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2791 wakaba 1.179 $self->{nc} == 0x005B) { # [
2792 wakaba 1.159
2793     $self->{state} = MD_CDATA_STATE;
2794 wakaba 1.179 $self->{s_kwd} = '[';
2795 wakaba 1.159
2796 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2797     $self->{line_prev} = $self->{line};
2798     $self->{column_prev} = $self->{column};
2799     $self->{column}++;
2800 wakaba 1.179 $self->{nc}
2801 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2802     } else {
2803 wakaba 1.179 $self->{set_nc}->($self);
2804 wakaba 1.175 }
2805 wakaba 1.1
2806 wakaba 1.159 redo A;
2807 wakaba 1.1 } else {
2808 wakaba 1.159
2809 wakaba 1.1 }
2810 wakaba 1.159
2811     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2812     line => $self->{line_prev},
2813 wakaba 1.160 column => $self->{column_prev} - 1);
2814 wakaba 1.159 ## Reconsume.
2815     $self->{state} = BOGUS_COMMENT_STATE;
2816 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN, data => '',
2817 wakaba 1.159 line => $self->{line_prev},
2818     column => $self->{column_prev} - 1,
2819     };
2820     redo A;
2821     } elsif ($self->{state} == MD_HYPHEN_STATE) {
2822 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
2823 wakaba 1.159
2824 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN, data => '',
2825 wakaba 1.159 line => $self->{line_prev},
2826     column => $self->{column_prev} - 2,
2827     };
2828     $self->{state} = COMMENT_START_STATE;
2829    
2830 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2831     $self->{line_prev} = $self->{line};
2832     $self->{column_prev} = $self->{column};
2833     $self->{column}++;
2834 wakaba 1.179 $self->{nc}
2835 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2836     } else {
2837 wakaba 1.179 $self->{set_nc}->($self);
2838 wakaba 1.175 }
2839 wakaba 1.1
2840 wakaba 1.159 redo A;
2841 wakaba 1.1 } else {
2842 wakaba 1.159
2843     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2844     line => $self->{line_prev},
2845     column => $self->{column_prev} - 2);
2846     $self->{state} = BOGUS_COMMENT_STATE;
2847     ## Reconsume.
2848 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN,
2849 wakaba 1.159 data => '-',
2850     line => $self->{line_prev},
2851     column => $self->{column_prev} - 2,
2852     };
2853     redo A;
2854 wakaba 1.1 }
2855 wakaba 1.159 } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2856     ## ASCII case-insensitive.
2857 wakaba 1.179 if ($self->{nc} == [
2858 wakaba 1.159 undef,
2859     0x004F, # O
2860     0x0043, # C
2861     0x0054, # T
2862     0x0059, # Y
2863     0x0050, # P
2864 wakaba 1.179 ]->[length $self->{s_kwd}] or
2865     $self->{nc} == [
2866 wakaba 1.159 undef,
2867     0x006F, # o
2868     0x0063, # c
2869     0x0074, # t
2870     0x0079, # y
2871     0x0070, # p
2872 wakaba 1.179 ]->[length $self->{s_kwd}]) {
2873 wakaba 1.159
2874     ## Stay in the state.
2875 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
2876 wakaba 1.159
2877 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2878     $self->{line_prev} = $self->{line};
2879     $self->{column_prev} = $self->{column};
2880     $self->{column}++;
2881 wakaba 1.179 $self->{nc}
2882 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2883     } else {
2884 wakaba 1.179 $self->{set_nc}->($self);
2885 wakaba 1.175 }
2886 wakaba 1.1
2887 wakaba 1.159 redo A;
2888 wakaba 1.179 } elsif ((length $self->{s_kwd}) == 6 and
2889     ($self->{nc} == 0x0045 or # E
2890     $self->{nc} == 0x0065)) { # e
2891 wakaba 1.159
2892     $self->{state} = DOCTYPE_STATE;
2893 wakaba 1.179 $self->{ct} = {type => DOCTYPE_TOKEN,
2894 wakaba 1.159 quirks => 1,
2895     line => $self->{line_prev},
2896     column => $self->{column_prev} - 7,
2897     };
2898 wakaba 1.124
2899 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2900     $self->{line_prev} = $self->{line};
2901     $self->{column_prev} = $self->{column};
2902     $self->{column}++;
2903 wakaba 1.179 $self->{nc}
2904 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2905     } else {
2906 wakaba 1.179 $self->{set_nc}->($self);
2907 wakaba 1.175 }
2908 wakaba 1.124
2909 wakaba 1.159 redo A;
2910 wakaba 1.124 } else {
2911    
2912 wakaba 1.159 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2913     line => $self->{line_prev},
2914 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd});
2915 wakaba 1.159 $self->{state} = BOGUS_COMMENT_STATE;
2916     ## Reconsume.
2917 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN,
2918     data => $self->{s_kwd},
2919 wakaba 1.159 line => $self->{line_prev},
2920 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd},
2921 wakaba 1.159 };
2922     redo A;
2923 wakaba 1.124 }
2924 wakaba 1.159 } elsif ($self->{state} == MD_CDATA_STATE) {
2925 wakaba 1.179 if ($self->{nc} == {
2926 wakaba 1.159 '[' => 0x0043, # C
2927     '[C' => 0x0044, # D
2928     '[CD' => 0x0041, # A
2929     '[CDA' => 0x0054, # T
2930     '[CDAT' => 0x0041, # A
2931 wakaba 1.179 }->{$self->{s_kwd}}) {
2932 wakaba 1.159
2933     ## Stay in the state.
2934 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
2935 wakaba 1.159
2936 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2937     $self->{line_prev} = $self->{line};
2938     $self->{column_prev} = $self->{column};
2939     $self->{column}++;
2940 wakaba 1.179 $self->{nc}
2941 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2942     } else {
2943 wakaba 1.179 $self->{set_nc}->($self);
2944 wakaba 1.175 }
2945 wakaba 1.124
2946 wakaba 1.159 redo A;
2947 wakaba 1.179 } elsif ($self->{s_kwd} eq '[CDATA' and
2948     $self->{nc} == 0x005B) { # [
2949 wakaba 1.159
2950 wakaba 1.179 $self->{ct} = {type => CHARACTER_TOKEN,
2951 wakaba 1.161 data => '',
2952     line => $self->{line_prev},
2953     column => $self->{column_prev} - 7};
2954     $self->{state} = CDATA_SECTION_STATE;
2955 wakaba 1.159
2956 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2957     $self->{line_prev} = $self->{line};
2958     $self->{column_prev} = $self->{column};
2959     $self->{column}++;
2960 wakaba 1.179 $self->{nc}
2961 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2962     } else {
2963 wakaba 1.179 $self->{set_nc}->($self);
2964 wakaba 1.175 }
2965 wakaba 1.124
2966 wakaba 1.159 redo A;
2967 wakaba 1.77 } else {
2968    
2969 wakaba 1.159 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment',
2970     line => $self->{line_prev},
2971 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd});
2972 wakaba 1.159 $self->{state} = BOGUS_COMMENT_STATE;
2973     ## Reconsume.
2974 wakaba 1.179 $self->{ct} = {type => COMMENT_TOKEN,
2975     data => $self->{s_kwd},
2976 wakaba 1.159 line => $self->{line_prev},
2977 wakaba 1.179 column => $self->{column_prev} - 1 - length $self->{s_kwd},
2978 wakaba 1.159 };
2979     redo A;
2980 wakaba 1.1 }
2981 wakaba 1.59 } elsif ($self->{state} == COMMENT_START_STATE) {
2982 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
2983 wakaba 1.77
2984 wakaba 1.59 $self->{state} = COMMENT_START_DASH_STATE;
2985 wakaba 1.23
2986 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
2987     $self->{line_prev} = $self->{line};
2988     $self->{column_prev} = $self->{column};
2989     $self->{column}++;
2990 wakaba 1.179 $self->{nc}
2991 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
2992     } else {
2993 wakaba 1.179 $self->{set_nc}->($self);
2994 wakaba 1.175 }
2995 wakaba 1.23
2996     redo A;
2997 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
2998 wakaba 1.77
2999 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment');
3000 wakaba 1.59 $self->{state} = DATA_STATE;
3001 wakaba 1.23
3002 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3003     $self->{line_prev} = $self->{line};
3004     $self->{column_prev} = $self->{column};
3005     $self->{column}++;
3006 wakaba 1.179 $self->{nc}
3007 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3008     } else {
3009 wakaba 1.179 $self->{set_nc}->($self);
3010 wakaba 1.175 }
3011 wakaba 1.23
3012    
3013 wakaba 1.179 return ($self->{ct}); # comment
3014 wakaba 1.23
3015     redo A;
3016 wakaba 1.179 } elsif ($self->{nc} == -1) {
3017 wakaba 1.77
3018 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3019 wakaba 1.59 $self->{state} = DATA_STATE;
3020 wakaba 1.23 ## reconsume
3021    
3022 wakaba 1.179 return ($self->{ct}); # comment
3023 wakaba 1.23
3024 wakaba 1.77 redo A;
3025     } else {
3026    
3027 wakaba 1.179 $self->{ct}->{data} # comment
3028     .= chr ($self->{nc});
3029 wakaba 1.59 $self->{state} = COMMENT_STATE;
3030 wakaba 1.23
3031 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3032     $self->{line_prev} = $self->{line};
3033     $self->{column_prev} = $self->{column};
3034     $self->{column}++;
3035 wakaba 1.179 $self->{nc}
3036 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3037     } else {
3038 wakaba 1.179 $self->{set_nc}->($self);
3039 wakaba 1.175 }
3040 wakaba 1.23
3041     redo A;
3042     }
3043 wakaba 1.59 } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
3044 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
3045 wakaba 1.77
3046 wakaba 1.59 $self->{state} = COMMENT_END_STATE;
3047 wakaba 1.23
3048 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3049     $self->{line_prev} = $self->{line};
3050     $self->{column_prev} = $self->{column};
3051     $self->{column}++;
3052 wakaba 1.179 $self->{nc}
3053 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3054     } else {
3055 wakaba 1.179 $self->{set_nc}->($self);
3056 wakaba 1.175 }
3057 wakaba 1.23
3058     redo A;
3059 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3060 wakaba 1.77
3061 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bogus comment');
3062 wakaba 1.59 $self->{state} = DATA_STATE;
3063 wakaba 1.23
3064 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3065     $self->{line_prev} = $self->{line};
3066     $self->{column_prev} = $self->{column};
3067     $self->{column}++;
3068 wakaba 1.179 $self->{nc}
3069 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3070     } else {
3071 wakaba 1.179 $self->{set_nc}->($self);
3072 wakaba 1.175 }
3073 wakaba 1.23
3074    
3075 wakaba 1.179 return ($self->{ct}); # comment
3076 wakaba 1.23
3077     redo A;
3078 wakaba 1.179 } elsif ($self->{nc} == -1) {
3079 wakaba 1.77
3080 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3081 wakaba 1.59 $self->{state} = DATA_STATE;
3082 wakaba 1.23 ## reconsume
3083    
3084 wakaba 1.179 return ($self->{ct}); # comment
3085 wakaba 1.23
3086     redo A;
3087     } else {
3088 wakaba 1.77
3089 wakaba 1.179 $self->{ct}->{data} # comment
3090     .= '-' . chr ($self->{nc});
3091 wakaba 1.59 $self->{state} = COMMENT_STATE;
3092 wakaba 1.23
3093 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3094     $self->{line_prev} = $self->{line};
3095     $self->{column_prev} = $self->{column};
3096     $self->{column}++;
3097 wakaba 1.179 $self->{nc}
3098 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3099     } else {
3100 wakaba 1.179 $self->{set_nc}->($self);
3101 wakaba 1.175 }
3102 wakaba 1.23
3103     redo A;
3104     }
3105 wakaba 1.59 } elsif ($self->{state} == COMMENT_STATE) {
3106 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
3107 wakaba 1.77
3108 wakaba 1.59 $self->{state} = COMMENT_END_DASH_STATE;
3109 wakaba 1.1
3110 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3111     $self->{line_prev} = $self->{line};
3112     $self->{column_prev} = $self->{column};
3113     $self->{column}++;
3114 wakaba 1.179 $self->{nc}
3115 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3116     } else {
3117 wakaba 1.179 $self->{set_nc}->($self);
3118 wakaba 1.175 }
3119 wakaba 1.1
3120     redo A;
3121 wakaba 1.179 } elsif ($self->{nc} == -1) {
3122 wakaba 1.77
3123 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3124 wakaba 1.59 $self->{state} = DATA_STATE;
3125 wakaba 1.1 ## reconsume
3126    
3127 wakaba 1.179 return ($self->{ct}); # comment
3128 wakaba 1.1
3129     redo A;
3130     } else {
3131 wakaba 1.77
3132 wakaba 1.179 $self->{ct}->{data} .= chr ($self->{nc}); # comment
3133     $self->{read_until}->($self->{ct}->{data},
3134 wakaba 1.169 q[-],
3135 wakaba 1.179 length $self->{ct}->{data});
3136 wakaba 1.169
3137 wakaba 1.1 ## Stay in the state
3138    
3139 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3140     $self->{line_prev} = $self->{line};
3141     $self->{column_prev} = $self->{column};
3142     $self->{column}++;
3143 wakaba 1.179 $self->{nc}
3144 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3145     } else {
3146 wakaba 1.179 $self->{set_nc}->($self);
3147 wakaba 1.175 }
3148 wakaba 1.1
3149     redo A;
3150     }
3151 wakaba 1.59 } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
3152 wakaba 1.179 if ($self->{nc} == 0x002D) { # -
3153 wakaba 1.77
3154 wakaba 1.59 $self->{state} = COMMENT_END_STATE;
3155 wakaba 1.1
3156 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3157     $self->{line_prev} = $self->{line};
3158     $self->{column_prev} = $self->{column};
3159     $self->{column}++;
3160 wakaba 1.179 $self->{nc}
3161 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3162     } else {
3163 wakaba 1.179 $self->{set_nc}->($self);
3164 wakaba 1.175 }
3165 wakaba 1.1
3166     redo A;
3167 wakaba 1.179 } elsif ($self->{nc} == -1) {
3168 wakaba 1.77
3169 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3170 wakaba 1.59 $self->{state} = DATA_STATE;
3171 wakaba 1.1 ## reconsume
3172    
3173 wakaba 1.179 return ($self->{ct}); # comment
3174 wakaba 1.1
3175     redo A;
3176     } else {
3177 wakaba 1.77
3178 wakaba 1.179 $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
3179 wakaba 1.59 $self->{state} = COMMENT_STATE;
3180 wakaba 1.1
3181 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3182     $self->{line_prev} = $self->{line};
3183     $self->{column_prev} = $self->{column};
3184     $self->{column}++;
3185 wakaba 1.179 $self->{nc}
3186 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3187     } else {
3188 wakaba 1.179 $self->{set_nc}->($self);
3189 wakaba 1.175 }
3190 wakaba 1.1
3191     redo A;
3192     }
3193 wakaba 1.59 } elsif ($self->{state} == COMMENT_END_STATE) {
3194 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
3195 wakaba 1.77
3196 wakaba 1.59 $self->{state} = DATA_STATE;
3197 wakaba 1.1
3198 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3199     $self->{line_prev} = $self->{line};
3200     $self->{column_prev} = $self->{column};
3201     $self->{column}++;
3202 wakaba 1.179 $self->{nc}
3203 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3204     } else {
3205 wakaba 1.179 $self->{set_nc}->($self);
3206 wakaba 1.175 }
3207 wakaba 1.1
3208    
3209 wakaba 1.179 return ($self->{ct}); # comment
3210 wakaba 1.1
3211     redo A;
3212 wakaba 1.179 } elsif ($self->{nc} == 0x002D) { # -
3213 wakaba 1.77
3214 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'dash in comment',
3215 wakaba 1.112 line => $self->{line_prev},
3216     column => $self->{column_prev});
3217 wakaba 1.179 $self->{ct}->{data} .= '-'; # comment
3218 wakaba 1.1 ## Stay in the state
3219    
3220 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3221     $self->{line_prev} = $self->{line};
3222     $self->{column_prev} = $self->{column};
3223     $self->{column}++;
3224 wakaba 1.179 $self->{nc}
3225 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3226     } else {
3227 wakaba 1.179 $self->{set_nc}->($self);
3228 wakaba 1.175 }
3229 wakaba 1.1
3230     redo A;
3231 wakaba 1.179 } elsif ($self->{nc} == -1) {
3232 wakaba 1.77
3233 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed comment');
3234 wakaba 1.59 $self->{state} = DATA_STATE;
3235 wakaba 1.1 ## reconsume
3236    
3237 wakaba 1.179 return ($self->{ct}); # comment
3238 wakaba 1.1
3239     redo A;
3240     } else {
3241 wakaba 1.77
3242 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'dash in comment',
3243 wakaba 1.112 line => $self->{line_prev},
3244     column => $self->{column_prev});
3245 wakaba 1.179 $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
3246 wakaba 1.59 $self->{state} = COMMENT_STATE;
3247 wakaba 1.1
3248 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3249     $self->{line_prev} = $self->{line};
3250     $self->{column_prev} = $self->{column};
3251     $self->{column}++;
3252 wakaba 1.179 $self->{nc}
3253 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3254     } else {
3255 wakaba 1.179 $self->{set_nc}->($self);
3256 wakaba 1.175 }
3257 wakaba 1.1
3258     redo A;
3259     }
3260 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_STATE) {
3261 wakaba 1.183 if ($is_space->{$self->{nc}}) {
3262 wakaba 1.77
3263 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
3264 wakaba 1.1
3265 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3266     $self->{line_prev} = $self->{line};
3267     $self->{column_prev} = $self->{column};
3268     $self->{column}++;
3269 wakaba 1.179 $self->{nc}
3270 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3271     } else {
3272 wakaba 1.179 $self->{set_nc}->($self);
3273 wakaba 1.175 }
3274 wakaba 1.1
3275     redo A;
3276     } else {
3277 wakaba 1.77
3278 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no space before DOCTYPE name');
3279 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
3280 wakaba 1.1 ## reconsume
3281     redo A;
3282     }
3283 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
3284 wakaba 1.183 if ($is_space->{$self->{nc}}) {
3285 wakaba 1.77
3286 wakaba 1.1 ## Stay in the state
3287    
3288 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3289     $self->{line_prev} = $self->{line};
3290     $self->{column_prev} = $self->{column};
3291     $self->{column}++;
3292 wakaba 1.179 $self->{nc}
3293 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3294     } else {
3295 wakaba 1.179 $self->{set_nc}->($self);
3296 wakaba 1.175 }
3297 wakaba 1.1
3298     redo A;
3299 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3300 wakaba 1.77
3301 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE name');
3302 wakaba 1.59 $self->{state} = DATA_STATE;
3303 wakaba 1.18
3304 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3305     $self->{line_prev} = $self->{line};
3306     $self->{column_prev} = $self->{column};
3307     $self->{column}++;
3308 wakaba 1.179 $self->{nc}
3309 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3310     } else {
3311 wakaba 1.179 $self->{set_nc}->($self);
3312 wakaba 1.175 }
3313 wakaba 1.18
3314    
3315 wakaba 1.179 return ($self->{ct}); # DOCTYPE (quirks)
3316 wakaba 1.18
3317     redo A;
3318 wakaba 1.179 } elsif ($self->{nc} == -1) {
3319 wakaba 1.77
3320 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE name');
3321 wakaba 1.59 $self->{state} = DATA_STATE;
3322 wakaba 1.18 ## reconsume
3323    
3324 wakaba 1.179 return ($self->{ct}); # DOCTYPE (quirks)
3325 wakaba 1.18
3326     redo A;
3327     } else {
3328 wakaba 1.77
3329 wakaba 1.179 $self->{ct}->{name} = chr $self->{nc};
3330     delete $self->{ct}->{quirks};
3331 wakaba 1.59 $self->{state} = DOCTYPE_NAME_STATE;
3332 wakaba 1.1
3333 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3334     $self->{line_prev} = $self->{line};
3335     $self->{column_prev} = $self->{column};
3336     $self->{column}++;
3337 wakaba 1.179 $self->{nc}
3338 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3339     } else {
3340 wakaba 1.179 $self->{set_nc}->($self);
3341 wakaba 1.175 }
3342 wakaba 1.1
3343     redo A;
3344 wakaba 1.18 }
3345 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
3346 wakaba 1.18 ## ISSUE: Redundant "First," in the spec.
3347 wakaba 1.183 if ($is_space->{$self->{nc}}) {
3348 wakaba 1.77
3349 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_NAME_STATE;
3350 wakaba 1.18
3351 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3352     $self->{line_prev} = $self->{line};
3353     $self->{column_prev} = $self->{column};
3354     $self->{column}++;
3355 wakaba 1.179 $self->{nc}
3356 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3357     } else {
3358 wakaba 1.179 $self->{set_nc}->($self);
3359 wakaba 1.175 }
3360 wakaba 1.18
3361     redo A;
3362 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3363 wakaba 1.77
3364 wakaba 1.59 $self->{state} = DATA_STATE;
3365 wakaba 1.1
3366 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3367     $self->{line_prev} = $self->{line};
3368     $self->{column_prev} = $self->{column};
3369     $self->{column}++;
3370 wakaba 1.179 $self->{nc}
3371 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3372     } else {
3373 wakaba 1.179 $self->{set_nc}->($self);
3374 wakaba 1.175 }
3375 wakaba 1.1
3376    
3377 wakaba 1.179 return ($self->{ct}); # DOCTYPE
3378 wakaba 1.1
3379     redo A;
3380 wakaba 1.179 } elsif ($self->{nc} == -1) {
3381 wakaba 1.77
3382 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3383 wakaba 1.59 $self->{state} = DATA_STATE;
3384 wakaba 1.1 ## reconsume
3385    
3386 wakaba 1.179 $self->{ct}->{quirks} = 1;
3387     return ($self->{ct}); # DOCTYPE
3388 wakaba 1.1
3389     redo A;
3390     } else {
3391 wakaba 1.77
3392 wakaba 1.179 $self->{ct}->{name}
3393     .= chr ($self->{nc}); # DOCTYPE
3394 wakaba 1.18 ## Stay in the state
3395 wakaba 1.1
3396 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3397     $self->{line_prev} = $self->{line};
3398     $self->{column_prev} = $self->{column};
3399     $self->{column}++;
3400 wakaba 1.179 $self->{nc}
3401 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3402     } else {
3403 wakaba 1.179 $self->{set_nc}->($self);
3404 wakaba 1.175 }
3405 wakaba 1.1
3406     redo A;
3407     }
3408 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
3409 wakaba 1.183 if ($is_space->{$self->{nc}}) {
3410 wakaba 1.77
3411 wakaba 1.18 ## Stay in the state
3412 wakaba 1.1
3413 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3414     $self->{line_prev} = $self->{line};
3415     $self->{column_prev} = $self->{column};
3416     $self->{column}++;
3417 wakaba 1.179 $self->{nc}
3418 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3419     } else {
3420 wakaba 1.179 $self->{set_nc}->($self);
3421 wakaba 1.175 }
3422 wakaba 1.1
3423     redo A;
3424 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3425 wakaba 1.77
3426 wakaba 1.59 $self->{state} = DATA_STATE;
3427 wakaba 1.1
3428 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3429     $self->{line_prev} = $self->{line};
3430     $self->{column_prev} = $self->{column};
3431     $self->{column}++;
3432 wakaba 1.179 $self->{nc}
3433 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3434     } else {
3435 wakaba 1.179 $self->{set_nc}->($self);
3436 wakaba 1.175 }
3437 wakaba 1.1
3438    
3439 wakaba 1.179 return ($self->{ct}); # DOCTYPE
3440 wakaba 1.1
3441     redo A;
3442 wakaba 1.179 } elsif ($self->{nc} == -1) {
3443 wakaba 1.77
3444 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3445 wakaba 1.59 $self->{state} = DATA_STATE;
3446 wakaba 1.18 ## reconsume
3447    
3448 wakaba 1.179 $self->{ct}->{quirks} = 1;
3449     return ($self->{ct}); # DOCTYPE
3450 wakaba 1.18
3451     redo A;
3452 wakaba 1.179 } elsif ($self->{nc} == 0x0050 or # P
3453     $self->{nc} == 0x0070) { # p
3454 wakaba 1.162 $self->{state} = PUBLIC_STATE;
3455 wakaba 1.179 $self->{s_kwd} = chr $self->{nc};
3456 wakaba 1.18
3457 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3458     $self->{line_prev} = $self->{line};
3459     $self->{column_prev} = $self->{column};
3460     $self->{column}++;
3461 wakaba 1.179 $self->{nc}
3462 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3463     } else {
3464 wakaba 1.179 $self->{set_nc}->($self);
3465 wakaba 1.175 }
3466 wakaba 1.18
3467 wakaba 1.162 redo A;
3468 wakaba 1.179 } elsif ($self->{nc} == 0x0053 or # S
3469     $self->{nc} == 0x0073) { # s
3470 wakaba 1.162 $self->{state} = SYSTEM_STATE;
3471 wakaba 1.179 $self->{s_kwd} = chr $self->{nc};
3472 wakaba 1.162
3473 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3474     $self->{line_prev} = $self->{line};
3475     $self->{column_prev} = $self->{column};
3476     $self->{column}++;
3477 wakaba 1.179 $self->{nc}
3478 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3479     } else {
3480 wakaba 1.179 $self->{set_nc}->($self);
3481 wakaba 1.175 }
3482 wakaba 1.18
3483 wakaba 1.162 redo A;
3484 wakaba 1.18 } else {
3485 wakaba 1.162
3486     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name');
3487 wakaba 1.179 $self->{ct}->{quirks} = 1;
3488 wakaba 1.18
3489 wakaba 1.162 $self->{state} = BOGUS_DOCTYPE_STATE;
3490 wakaba 1.18
3491 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3492     $self->{line_prev} = $self->{line};
3493     $self->{column_prev} = $self->{column};
3494     $self->{column}++;
3495 wakaba 1.179 $self->{nc}
3496 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3497     } else {
3498 wakaba 1.179 $self->{set_nc}->($self);
3499 wakaba 1.175 }
3500 wakaba 1.18
3501 wakaba 1.162 redo A;
3502 wakaba 1.18 }
3503 wakaba 1.162 } elsif ($self->{state} == PUBLIC_STATE) {
3504     ## ASCII case-insensitive
3505 wakaba 1.179 if ($self->{nc} == [
3506 wakaba 1.162 undef,
3507     0x0055, # U
3508     0x0042, # B
3509     0x004C, # L
3510     0x0049, # I
3511 wakaba 1.179 ]->[length $self->{s_kwd}] or
3512     $self->{nc} == [
3513 wakaba 1.162 undef,
3514     0x0075, # u
3515     0x0062, # b
3516     0x006C, # l
3517     0x0069, # i
3518 wakaba 1.179 ]->[length $self->{s_kwd}]) {
3519 wakaba 1.162
3520     ## Stay in the state.
3521 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
3522 wakaba 1.162
3523 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3524     $self->{line_prev} = $self->{line};
3525     $self->{column_prev} = $self->{column};
3526     $self->{column}++;
3527 wakaba 1.179 $self->{nc}
3528 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3529     } else {
3530 wakaba 1.179 $self->{set_nc}->($self);
3531 wakaba 1.175 }
3532 wakaba 1.18
3533 wakaba 1.162 redo A;
3534 wakaba 1.179 } elsif ((length $self->{s_kwd}) == 5 and
3535     ($self->{nc} == 0x0043 or # C
3536     $self->{nc} == 0x0063)) { # c
3537 wakaba 1.162
3538     $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3539    
3540 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3541     $self->{line_prev} = $self->{line};
3542     $self->{column_prev} = $self->{column};
3543     $self->{column}++;
3544 wakaba 1.179 $self->{nc}
3545 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3546     } else {
3547 wakaba 1.179 $self->{set_nc}->($self);
3548 wakaba 1.175 }
3549 wakaba 1.18
3550 wakaba 1.162 redo A;
3551 wakaba 1.18 } else {
3552 wakaba 1.162
3553     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name',
3554     line => $self->{line_prev},
3555 wakaba 1.179 column => $self->{column_prev} + 1 - length $self->{s_kwd});
3556     $self->{ct}->{quirks} = 1;
3557 wakaba 1.162
3558     $self->{state} = BOGUS_DOCTYPE_STATE;
3559     ## Reconsume.
3560     redo A;
3561 wakaba 1.18 }
3562 wakaba 1.162 } elsif ($self->{state} == SYSTEM_STATE) {
3563     ## ASCII case-insensitive
3564 wakaba 1.179 if ($self->{nc} == [
3565 wakaba 1.162 undef,
3566     0x0059, # Y
3567     0x0053, # S
3568     0x0054, # T
3569     0x0045, # E
3570 wakaba 1.179 ]->[length $self->{s_kwd}] or
3571     $self->{nc} == [
3572 wakaba 1.162 undef,
3573     0x0079, # y
3574     0x0073, # s
3575     0x0074, # t
3576     0x0065, # e
3577 wakaba 1.179 ]->[length $self->{s_kwd}]) {
3578 wakaba 1.162
3579     ## Stay in the state.
3580 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
3581 wakaba 1.162
3582 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3583     $self->{line_prev} = $self->{line};
3584     $self->{column_prev} = $self->{column};
3585     $self->{column}++;
3586 wakaba 1.179 $self->{nc}
3587 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3588     } else {
3589 wakaba 1.179 $self->{set_nc}->($self);
3590 wakaba 1.175 }
3591 wakaba 1.18
3592 wakaba 1.162 redo A;
3593 wakaba 1.179 } elsif ((length $self->{s_kwd}) == 5 and
3594     ($self->{nc} == 0x004D or # M
3595     $self->{nc} == 0x006D)) { # m
3596 wakaba 1.18
3597 wakaba 1.162 $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
3598 wakaba 1.77
3599 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3600     $self->{line_prev} = $self->{line};
3601     $self->{column_prev} = $self->{column};
3602     $self->{column}++;
3603 wakaba 1.179 $self->{nc}
3604 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3605     } else {
3606 wakaba 1.179 $self->{set_nc}->($self);
3607 wakaba 1.175 }
3608 wakaba 1.18
3609 wakaba 1.162 redo A;
3610     } else {
3611    
3612     $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after DOCTYPE name',
3613     line => $self->{line_prev},
3614 wakaba 1.179 column => $self->{column_prev} + 1 - length $self->{s_kwd});
3615     $self->{ct}->{quirks} = 1;
3616 wakaba 1.162
3617     $self->{state} = BOGUS_DOCTYPE_STATE;
3618     ## Reconsume.
3619     redo A;
3620 wakaba 1.18 }
3621 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
3622 wakaba 1.183 if ($is_space->{$self->{nc}}) {
3623 wakaba 1.77
3624 wakaba 1.1 ## Stay in the state
3625    
3626 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3627     $self->{line_prev} = $self->{line};
3628     $self->{column_prev} = $self->{column};
3629     $self->{column}++;
3630 wakaba 1.179 $self->{nc}
3631 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3632     } else {
3633 wakaba 1.179 $self->{set_nc}->($self);
3634 wakaba 1.175 }
3635 wakaba 1.1
3636     redo A;
3637 wakaba 1.179 } elsif ($self->{nc} eq 0x0022) { # "
3638 wakaba 1.77
3639 wakaba 1.179 $self->{ct}->{pubid} = ''; # DOCTYPE
3640 wakaba 1.59 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
3641 wakaba 1.18
3642 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3643     $self->{line_prev} = $self->{line};
3644     $self->{column_prev} = $self->{column};
3645     $self->{column}++;
3646 wakaba 1.179 $self->{nc}
3647 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3648     } else {
3649 wakaba 1.179 $self->{set_nc}->($self);
3650 wakaba 1.175 }
3651 wakaba 1.18
3652     redo A;
3653 wakaba 1.179 } elsif ($self->{nc} eq 0x0027) { # '
3654 wakaba 1.77
3655 wakaba 1.179 $self->{ct}->{pubid} = ''; # DOCTYPE
3656 wakaba 1.59 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
3657 wakaba 1.18
3658 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3659     $self->{line_prev} = $self->{line};
3660     $self->{column_prev} = $self->{column};
3661     $self->{column}++;
3662 wakaba 1.179 $self->{nc}
3663 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3664     } else {
3665 wakaba 1.179 $self->{set_nc}->($self);
3666 wakaba 1.175 }
3667 wakaba 1.18
3668     redo A;
3669 wakaba 1.179 } elsif ($self->{nc} eq 0x003E) { # >
3670 wakaba 1.77
3671 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no PUBLIC literal');
3672 wakaba 1.18
3673 wakaba 1.59 $self->{state} = DATA_STATE;
3674 wakaba 1.18
3675 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3676     $self->{line_prev} = $self->{line};
3677     $self->{column_prev} = $self->{column};
3678     $self->{column}++;
3679 wakaba 1.179 $self->{nc}
3680 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3681     } else {
3682 wakaba 1.179 $self->{set_nc}->($self);
3683 wakaba 1.175 }
3684 wakaba 1.18
3685    
3686 wakaba 1.179 $self->{ct}->{quirks} = 1;
3687     return ($self->{ct}); # DOCTYPE
3688 wakaba 1.18
3689     redo A;
3690 wakaba 1.179 } elsif ($self->{nc} == -1) {
3691 wakaba 1.77
3692 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3693 wakaba 1.18
3694 wakaba 1.59 $self->{state} = DATA_STATE;
3695 wakaba 1.1 ## reconsume
3696    
3697 wakaba 1.179 $self->{ct}->{quirks} = 1;
3698     return ($self->{ct}); # DOCTYPE
3699 wakaba 1.1
3700     redo A;
3701     } else {
3702 wakaba 1.77
3703 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after PUBLIC');
3704 wakaba 1.179 $self->{ct}->{quirks} = 1;
3705 wakaba 1.73
3706 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3707 wakaba 1.18
3708 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3709     $self->{line_prev} = $self->{line};
3710     $self->{column_prev} = $self->{column};
3711     $self->{column}++;
3712 wakaba 1.179 $self->{nc}
3713 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3714     } else {
3715 wakaba 1.179 $self->{set_nc}->($self);
3716 wakaba 1.175 }
3717 wakaba 1.18
3718     redo A;
3719     }
3720 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
3721 wakaba 1.179 if ($self->{nc} == 0x0022) { # "
3722 wakaba 1.77
3723 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3724 wakaba 1.18
3725 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3726     $self->{line_prev} = $self->{line};
3727     $self->{column_prev} = $self->{column};
3728     $self->{column}++;
3729 wakaba 1.179 $self->{nc}
3730 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3731     } else {
3732 wakaba 1.179 $self->{set_nc}->($self);
3733 wakaba 1.175 }
3734 wakaba 1.18
3735     redo A;
3736 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3737 wakaba 1.77
3738 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3739 wakaba 1.70
3740     $self->{state} = DATA_STATE;
3741    
3742 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3743     $self->{line_prev} = $self->{line};
3744     $self->{column_prev} = $self->{column};
3745     $self->{column}++;
3746 wakaba 1.179 $self->{nc}
3747 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3748     } else {
3749 wakaba 1.179 $self->{set_nc}->($self);
3750 wakaba 1.175 }
3751 wakaba 1.70
3752    
3753 wakaba 1.179 $self->{ct}->{quirks} = 1;
3754     return ($self->{ct}); # DOCTYPE
3755 wakaba 1.70
3756     redo A;
3757 wakaba 1.179 } elsif ($self->{nc} == -1) {
3758 wakaba 1.77
3759 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3760 wakaba 1.18
3761 wakaba 1.59 $self->{state} = DATA_STATE;
3762 wakaba 1.18 ## reconsume
3763    
3764 wakaba 1.179 $self->{ct}->{quirks} = 1;
3765     return ($self->{ct}); # DOCTYPE
3766 wakaba 1.18
3767     redo A;
3768     } else {
3769 wakaba 1.77
3770 wakaba 1.179 $self->{ct}->{pubid} # DOCTYPE
3771     .= chr $self->{nc};
3772     $self->{read_until}->($self->{ct}->{pubid}, q[">],
3773     length $self->{ct}->{pubid});
3774 wakaba 1.169
3775 wakaba 1.18 ## Stay in the state
3776    
3777 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3778     $self->{line_prev} = $self->{line};
3779     $self->{column_prev} = $self->{column};
3780     $self->{column}++;
3781 wakaba 1.179 $self->{nc}
3782 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3783     } else {
3784 wakaba 1.179 $self->{set_nc}->($self);
3785 wakaba 1.175 }
3786 wakaba 1.18
3787     redo A;
3788     }
3789 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
3790 wakaba 1.179 if ($self->{nc} == 0x0027) { # '
3791 wakaba 1.77
3792 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3793 wakaba 1.18
3794 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3795     $self->{line_prev} = $self->{line};
3796     $self->{column_prev} = $self->{column};
3797     $self->{column}++;
3798 wakaba 1.179 $self->{nc}
3799 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3800     } else {
3801 wakaba 1.179 $self->{set_nc}->($self);
3802 wakaba 1.175 }
3803 wakaba 1.18
3804     redo A;
3805 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3806 wakaba 1.77
3807 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3808 wakaba 1.70
3809     $self->{state} = DATA_STATE;
3810    
3811 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3812     $self->{line_prev} = $self->{line};
3813     $self->{column_prev} = $self->{column};
3814     $self->{column}++;
3815 wakaba 1.179 $self->{nc}
3816 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3817     } else {
3818 wakaba 1.179 $self->{set_nc}->($self);
3819 wakaba 1.175 }
3820 wakaba 1.70
3821    
3822 wakaba 1.179 $self->{ct}->{quirks} = 1;
3823     return ($self->{ct}); # DOCTYPE
3824 wakaba 1.70
3825     redo A;
3826 wakaba 1.179 } elsif ($self->{nc} == -1) {
3827 wakaba 1.77
3828 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed PUBLIC literal');
3829 wakaba 1.18
3830 wakaba 1.59 $self->{state} = DATA_STATE;
3831 wakaba 1.18 ## reconsume
3832    
3833 wakaba 1.179 $self->{ct}->{quirks} = 1;
3834     return ($self->{ct}); # DOCTYPE
3835 wakaba 1.18
3836     redo A;
3837     } else {
3838 wakaba 1.77
3839 wakaba 1.179 $self->{ct}->{pubid} # DOCTYPE
3840     .= chr $self->{nc};
3841     $self->{read_until}->($self->{ct}->{pubid}, q['>],
3842     length $self->{ct}->{pubid});
3843 wakaba 1.169
3844 wakaba 1.18 ## Stay in the state
3845    
3846 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3847     $self->{line_prev} = $self->{line};
3848     $self->{column_prev} = $self->{column};
3849     $self->{column}++;
3850 wakaba 1.179 $self->{nc}
3851 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3852     } else {
3853 wakaba 1.179 $self->{set_nc}->($self);
3854 wakaba 1.175 }
3855 wakaba 1.18
3856     redo A;
3857     }
3858 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
3859 wakaba 1.183 if ($is_space->{$self->{nc}}) {
3860 wakaba 1.77
3861 wakaba 1.1 ## Stay in the state
3862    
3863 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3864     $self->{line_prev} = $self->{line};
3865     $self->{column_prev} = $self->{column};
3866     $self->{column}++;
3867 wakaba 1.179 $self->{nc}
3868 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3869     } else {
3870 wakaba 1.179 $self->{set_nc}->($self);
3871 wakaba 1.175 }
3872 wakaba 1.1
3873     redo A;
3874 wakaba 1.179 } elsif ($self->{nc} == 0x0022) { # "
3875 wakaba 1.77
3876 wakaba 1.179 $self->{ct}->{sysid} = ''; # DOCTYPE
3877 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
3878 wakaba 1.18
3879 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3880     $self->{line_prev} = $self->{line};
3881     $self->{column_prev} = $self->{column};
3882     $self->{column}++;
3883 wakaba 1.179 $self->{nc}
3884 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3885     } else {
3886 wakaba 1.179 $self->{set_nc}->($self);
3887 wakaba 1.175 }
3888 wakaba 1.18
3889     redo A;
3890 wakaba 1.179 } elsif ($self->{nc} == 0x0027) { # '
3891 wakaba 1.77
3892 wakaba 1.179 $self->{ct}->{sysid} = ''; # DOCTYPE
3893 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
3894 wakaba 1.18
3895 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3896     $self->{line_prev} = $self->{line};
3897     $self->{column_prev} = $self->{column};
3898     $self->{column}++;
3899 wakaba 1.179 $self->{nc}
3900 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3901     } else {
3902 wakaba 1.179 $self->{set_nc}->($self);
3903 wakaba 1.175 }
3904 wakaba 1.18
3905     redo A;
3906 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
3907 wakaba 1.77
3908 wakaba 1.59 $self->{state} = DATA_STATE;
3909 wakaba 1.18
3910 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3911     $self->{line_prev} = $self->{line};
3912     $self->{column_prev} = $self->{column};
3913     $self->{column}++;
3914 wakaba 1.179 $self->{nc}
3915 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3916     } else {
3917 wakaba 1.179 $self->{set_nc}->($self);
3918 wakaba 1.175 }
3919 wakaba 1.18
3920    
3921 wakaba 1.179 return ($self->{ct}); # DOCTYPE
3922 wakaba 1.18
3923     redo A;
3924 wakaba 1.179 } elsif ($self->{nc} == -1) {
3925 wakaba 1.77
3926 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
3927 wakaba 1.18
3928 wakaba 1.59 $self->{state} = DATA_STATE;
3929 wakaba 1.26 ## reconsume
3930 wakaba 1.18
3931 wakaba 1.179 $self->{ct}->{quirks} = 1;
3932     return ($self->{ct}); # DOCTYPE
3933 wakaba 1.18
3934     redo A;
3935     } else {
3936 wakaba 1.77
3937 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after PUBLIC literal');
3938 wakaba 1.179 $self->{ct}->{quirks} = 1;
3939 wakaba 1.73
3940 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3941 wakaba 1.18
3942 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3943     $self->{line_prev} = $self->{line};
3944     $self->{column_prev} = $self->{column};
3945     $self->{column}++;
3946 wakaba 1.179 $self->{nc}
3947 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3948     } else {
3949 wakaba 1.179 $self->{set_nc}->($self);
3950 wakaba 1.175 }
3951 wakaba 1.18
3952     redo A;
3953 wakaba 1.1 }
3954 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
3955 wakaba 1.183 if ($is_space->{$self->{nc}}) {
3956 wakaba 1.77
3957 wakaba 1.1 ## Stay in the state
3958    
3959 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3960     $self->{line_prev} = $self->{line};
3961     $self->{column_prev} = $self->{column};
3962     $self->{column}++;
3963 wakaba 1.179 $self->{nc}
3964 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3965     } else {
3966 wakaba 1.179 $self->{set_nc}->($self);
3967 wakaba 1.175 }
3968 wakaba 1.1
3969     redo A;
3970 wakaba 1.179 } elsif ($self->{nc} == 0x0022) { # "
3971 wakaba 1.77
3972 wakaba 1.179 $self->{ct}->{sysid} = ''; # DOCTYPE
3973 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
3974 wakaba 1.18
3975 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3976     $self->{line_prev} = $self->{line};
3977     $self->{column_prev} = $self->{column};
3978     $self->{column}++;
3979 wakaba 1.179 $self->{nc}
3980 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3981     } else {
3982 wakaba 1.179 $self->{set_nc}->($self);
3983 wakaba 1.175 }
3984 wakaba 1.18
3985     redo A;
3986 wakaba 1.179 } elsif ($self->{nc} == 0x0027) { # '
3987 wakaba 1.77
3988 wakaba 1.179 $self->{ct}->{sysid} = ''; # DOCTYPE
3989 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
3990 wakaba 1.18
3991 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
3992     $self->{line_prev} = $self->{line};
3993     $self->{column_prev} = $self->{column};
3994     $self->{column}++;
3995 wakaba 1.179 $self->{nc}
3996 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
3997     } else {
3998 wakaba 1.179 $self->{set_nc}->($self);
3999 wakaba 1.175 }
4000 wakaba 1.18
4001     redo A;
4002 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
4003 wakaba 1.77
4004 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no SYSTEM literal');
4005 wakaba 1.59 $self->{state} = DATA_STATE;
4006 wakaba 1.1
4007 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4008     $self->{line_prev} = $self->{line};
4009     $self->{column_prev} = $self->{column};
4010     $self->{column}++;
4011 wakaba 1.179 $self->{nc}
4012 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4013     } else {
4014 wakaba 1.179 $self->{set_nc}->($self);
4015 wakaba 1.175 }
4016 wakaba 1.1
4017    
4018 wakaba 1.179 $self->{ct}->{quirks} = 1;
4019     return ($self->{ct}); # DOCTYPE
4020 wakaba 1.1
4021     redo A;
4022 wakaba 1.179 } elsif ($self->{nc} == -1) {
4023 wakaba 1.77
4024 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
4025 wakaba 1.18
4026 wakaba 1.59 $self->{state} = DATA_STATE;
4027 wakaba 1.26 ## reconsume
4028 wakaba 1.18
4029 wakaba 1.179 $self->{ct}->{quirks} = 1;
4030     return ($self->{ct}); # DOCTYPE
4031 wakaba 1.18
4032     redo A;
4033     } else {
4034 wakaba 1.77
4035 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after SYSTEM');
4036 wakaba 1.179 $self->{ct}->{quirks} = 1;
4037 wakaba 1.73
4038 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
4039 wakaba 1.18
4040 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4041     $self->{line_prev} = $self->{line};
4042     $self->{column_prev} = $self->{column};
4043     $self->{column}++;
4044 wakaba 1.179 $self->{nc}
4045 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4046     } else {
4047 wakaba 1.179 $self->{set_nc}->($self);
4048 wakaba 1.175 }
4049 wakaba 1.18
4050     redo A;
4051     }
4052 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
4053 wakaba 1.179 if ($self->{nc} == 0x0022) { # "
4054 wakaba 1.77
4055 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
4056 wakaba 1.18
4057 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4058     $self->{line_prev} = $self->{line};
4059     $self->{column_prev} = $self->{column};
4060     $self->{column}++;
4061 wakaba 1.179 $self->{nc}
4062 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4063     } else {
4064 wakaba 1.179 $self->{set_nc}->($self);
4065 wakaba 1.175 }
4066 wakaba 1.18
4067     redo A;
4068 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
4069 wakaba 1.77
4070 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4071 wakaba 1.70
4072     $self->{state} = DATA_STATE;
4073    
4074 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4075     $self->{line_prev} = $self->{line};
4076     $self->{column_prev} = $self->{column};
4077     $self->{column}++;
4078 wakaba 1.179 $self->{nc}
4079 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4080     } else {
4081 wakaba 1.179 $self->{set_nc}->($self);
4082 wakaba 1.175 }
4083 wakaba 1.70
4084    
4085 wakaba 1.179 $self->{ct}->{quirks} = 1;
4086     return ($self->{ct}); # DOCTYPE
4087 wakaba 1.70
4088     redo A;
4089 wakaba 1.179 } elsif ($self->{nc} == -1) {
4090 wakaba 1.77
4091 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4092 wakaba 1.18
4093 wakaba 1.59 $self->{state} = DATA_STATE;
4094 wakaba 1.1 ## reconsume
4095    
4096 wakaba 1.179 $self->{ct}->{quirks} = 1;
4097     return ($self->{ct}); # DOCTYPE
4098 wakaba 1.1
4099     redo A;
4100     } else {
4101 wakaba 1.77
4102 wakaba 1.179 $self->{ct}->{sysid} # DOCTYPE
4103     .= chr $self->{nc};
4104     $self->{read_until}->($self->{ct}->{sysid}, q[">],
4105     length $self->{ct}->{sysid});
4106 wakaba 1.169
4107 wakaba 1.18 ## Stay in the state
4108    
4109 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4110     $self->{line_prev} = $self->{line};
4111     $self->{column_prev} = $self->{column};
4112     $self->{column}++;
4113 wakaba 1.179 $self->{nc}
4114 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4115     } else {
4116 wakaba 1.179 $self->{set_nc}->($self);
4117 wakaba 1.175 }
4118 wakaba 1.18
4119     redo A;
4120     }
4121 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
4122 wakaba 1.179 if ($self->{nc} == 0x0027) { # '
4123 wakaba 1.77
4124 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
4125 wakaba 1.18
4126 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4127     $self->{line_prev} = $self->{line};
4128     $self->{column_prev} = $self->{column};
4129     $self->{column}++;
4130 wakaba 1.179 $self->{nc}
4131 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4132     } else {
4133 wakaba 1.179 $self->{set_nc}->($self);
4134 wakaba 1.175 }
4135 wakaba 1.18
4136     redo A;
4137 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
4138 wakaba 1.77
4139 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4140 wakaba 1.70
4141     $self->{state} = DATA_STATE;
4142    
4143 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4144     $self->{line_prev} = $self->{line};
4145     $self->{column_prev} = $self->{column};
4146     $self->{column}++;
4147 wakaba 1.179 $self->{nc}
4148 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4149     } else {
4150 wakaba 1.179 $self->{set_nc}->($self);
4151 wakaba 1.175 }
4152 wakaba 1.70
4153    
4154 wakaba 1.179 $self->{ct}->{quirks} = 1;
4155     return ($self->{ct}); # DOCTYPE
4156 wakaba 1.70
4157     redo A;
4158 wakaba 1.179 } elsif ($self->{nc} == -1) {
4159 wakaba 1.77
4160 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed SYSTEM literal');
4161 wakaba 1.18
4162 wakaba 1.59 $self->{state} = DATA_STATE;
4163 wakaba 1.18 ## reconsume
4164    
4165 wakaba 1.179 $self->{ct}->{quirks} = 1;
4166     return ($self->{ct}); # DOCTYPE
4167 wakaba 1.18
4168     redo A;
4169     } else {
4170 wakaba 1.77
4171 wakaba 1.179 $self->{ct}->{sysid} # DOCTYPE
4172     .= chr $self->{nc};
4173     $self->{read_until}->($self->{ct}->{sysid}, q['>],
4174     length $self->{ct}->{sysid});
4175 wakaba 1.169
4176 wakaba 1.18 ## Stay in the state
4177    
4178 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4179     $self->{line_prev} = $self->{line};
4180     $self->{column_prev} = $self->{column};
4181     $self->{column}++;
4182 wakaba 1.179 $self->{nc}
4183 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4184     } else {
4185 wakaba 1.179 $self->{set_nc}->($self);
4186 wakaba 1.175 }
4187 wakaba 1.18
4188     redo A;
4189     }
4190 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
4191 wakaba 1.183 if ($is_space->{$self->{nc}}) {
4192 wakaba 1.77
4193 wakaba 1.18 ## Stay in the state
4194    
4195 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4196     $self->{line_prev} = $self->{line};
4197     $self->{column_prev} = $self->{column};
4198     $self->{column}++;
4199 wakaba 1.179 $self->{nc}
4200 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4201     } else {
4202 wakaba 1.179 $self->{set_nc}->($self);
4203 wakaba 1.175 }
4204 wakaba 1.18
4205     redo A;
4206 wakaba 1.179 } elsif ($self->{nc} == 0x003E) { # >
4207 wakaba 1.77
4208 wakaba 1.59 $self->{state} = DATA_STATE;
4209 wakaba 1.18
4210 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4211     $self->{line_prev} = $self->{line};
4212     $self->{column_prev} = $self->{column};
4213     $self->{column}++;
4214 wakaba 1.179 $self->{nc}
4215 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4216     } else {
4217 wakaba 1.179 $self->{set_nc}->($self);
4218 wakaba 1.175 }
4219 wakaba 1.18
4220    
4221 wakaba 1.179 return ($self->{ct}); # DOCTYPE
4222 wakaba 1.18
4223     redo A;
4224 wakaba 1.179 } elsif ($self->{nc} == -1) {
4225 wakaba 1.77
4226 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unclosed DOCTYPE');
4227 wakaba 1.59 $self->{state} = DATA_STATE;
4228 wakaba 1.26 ## reconsume
4229 wakaba 1.18
4230 wakaba 1.179 $self->{ct}->{quirks} = 1;
4231     return ($self->{ct}); # DOCTYPE
4232 wakaba 1.18
4233     redo A;
4234     } else {
4235 wakaba 1.77
4236 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'string after SYSTEM literal');
4237 wakaba 1.179 #$self->{ct}->{quirks} = 1;
4238 wakaba 1.73
4239 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
4240 wakaba 1.1
4241 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4242     $self->{line_prev} = $self->{line};
4243     $self->{column_prev} = $self->{column};
4244     $self->{column}++;
4245 wakaba 1.179 $self->{nc}
4246 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4247     } else {
4248 wakaba 1.179 $self->{set_nc}->($self);
4249 wakaba 1.175 }
4250 wakaba 1.1
4251     redo A;
4252     }
4253 wakaba 1.59 } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
4254 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
4255 wakaba 1.77
4256 wakaba 1.59 $self->{state} = DATA_STATE;
4257 wakaba 1.1
4258 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4259     $self->{line_prev} = $self->{line};
4260     $self->{column_prev} = $self->{column};
4261     $self->{column}++;
4262 wakaba 1.179 $self->{nc}
4263 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4264     } else {
4265 wakaba 1.179 $self->{set_nc}->($self);
4266 wakaba 1.175 }
4267 wakaba 1.1
4268    
4269 wakaba 1.179 return ($self->{ct}); # DOCTYPE
4270 wakaba 1.1
4271     redo A;
4272 wakaba 1.179 } elsif ($self->{nc} == -1) {
4273 wakaba 1.77
4274 wakaba 1.59 $self->{state} = DATA_STATE;
4275 wakaba 1.1 ## reconsume
4276    
4277 wakaba 1.179 return ($self->{ct}); # DOCTYPE
4278 wakaba 1.1
4279     redo A;
4280     } else {
4281 wakaba 1.77
4282 wakaba 1.169 my $s = '';
4283     $self->{read_until}->($s, q[>], 0);
4284    
4285 wakaba 1.1 ## Stay in the state
4286    
4287 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4288     $self->{line_prev} = $self->{line};
4289     $self->{column_prev} = $self->{column};
4290     $self->{column}++;
4291 wakaba 1.179 $self->{nc}
4292 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4293     } else {
4294 wakaba 1.179 $self->{set_nc}->($self);
4295 wakaba 1.175 }
4296 wakaba 1.1
4297     redo A;
4298     }
4299 wakaba 1.161 } elsif ($self->{state} == CDATA_SECTION_STATE) {
4300     ## NOTE: "CDATA section state" in the state is jointly implemented
4301     ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
4302     ## and |CDATA_SECTION_MSE2_STATE|.
4303 wakaba 1.124
4304 wakaba 1.179 if ($self->{nc} == 0x005D) { # ]
4305 wakaba 1.161
4306     $self->{state} = CDATA_SECTION_MSE1_STATE;
4307    
4308 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4309     $self->{line_prev} = $self->{line};
4310     $self->{column_prev} = $self->{column};
4311     $self->{column}++;
4312 wakaba 1.179 $self->{nc}
4313 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4314     } else {
4315 wakaba 1.179 $self->{set_nc}->($self);
4316 wakaba 1.175 }
4317 wakaba 1.161
4318     redo A;
4319 wakaba 1.179 } elsif ($self->{nc} == -1) {
4320 wakaba 1.161 $self->{state} = DATA_STATE;
4321    
4322 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4323     $self->{line_prev} = $self->{line};
4324     $self->{column_prev} = $self->{column};
4325     $self->{column}++;
4326 wakaba 1.179 $self->{nc}
4327 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4328     } else {
4329 wakaba 1.179 $self->{set_nc}->($self);
4330 wakaba 1.175 }
4331 wakaba 1.124
4332 wakaba 1.179 if (length $self->{ct}->{data}) { # character
4333 wakaba 1.161
4334 wakaba 1.179 return ($self->{ct}); # character
4335 wakaba 1.161 } else {
4336    
4337 wakaba 1.179 ## No token to emit. $self->{ct} is discarded.
4338 wakaba 1.161 }
4339     redo A;
4340     } else {
4341    
4342 wakaba 1.179 $self->{ct}->{data} .= chr $self->{nc};
4343     $self->{read_until}->($self->{ct}->{data},
4344 wakaba 1.169 q<]>,
4345 wakaba 1.179 length $self->{ct}->{data});
4346 wakaba 1.169
4347 wakaba 1.161 ## Stay in the state.
4348    
4349 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4350     $self->{line_prev} = $self->{line};
4351     $self->{column_prev} = $self->{column};
4352     $self->{column}++;
4353 wakaba 1.179 $self->{nc}
4354 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4355     } else {
4356 wakaba 1.179 $self->{set_nc}->($self);
4357 wakaba 1.175 }
4358 wakaba 1.124
4359 wakaba 1.161 redo A;
4360     }
4361    
4362     ## ISSUE: "text tokens" in spec.
4363     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
4364 wakaba 1.179 if ($self->{nc} == 0x005D) { # ]
4365 wakaba 1.161
4366     $self->{state} = CDATA_SECTION_MSE2_STATE;
4367    
4368 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4369     $self->{line_prev} = $self->{line};
4370     $self->{column_prev} = $self->{column};
4371     $self->{column}++;
4372 wakaba 1.179 $self->{nc}
4373 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4374     } else {
4375 wakaba 1.179 $self->{set_nc}->($self);
4376 wakaba 1.175 }
4377 wakaba 1.124
4378 wakaba 1.161 redo A;
4379     } else {
4380    
4381 wakaba 1.179 $self->{ct}->{data} .= ']';
4382 wakaba 1.161 $self->{state} = CDATA_SECTION_STATE;
4383     ## Reconsume.
4384     redo A;
4385     }
4386     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
4387 wakaba 1.179 if ($self->{nc} == 0x003E) { # >
4388 wakaba 1.161 $self->{state} = DATA_STATE;
4389    
4390 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4391     $self->{line_prev} = $self->{line};
4392     $self->{column_prev} = $self->{column};
4393     $self->{column}++;
4394 wakaba 1.179 $self->{nc}
4395 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4396     } else {
4397 wakaba 1.179 $self->{set_nc}->($self);
4398 wakaba 1.175 }
4399 wakaba 1.124
4400 wakaba 1.179 if (length $self->{ct}->{data}) { # character
4401 wakaba 1.161
4402 wakaba 1.179 return ($self->{ct}); # character
4403 wakaba 1.124 } else {
4404    
4405 wakaba 1.179 ## No token to emit. $self->{ct} is discarded.
4406 wakaba 1.124 }
4407 wakaba 1.161 redo A;
4408 wakaba 1.179 } elsif ($self->{nc} == 0x005D) { # ]
4409 wakaba 1.161 # character
4410 wakaba 1.179 $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
4411 wakaba 1.161 ## Stay in the state.
4412 wakaba 1.124
4413 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4414     $self->{line_prev} = $self->{line};
4415     $self->{column_prev} = $self->{column};
4416     $self->{column}++;
4417 wakaba 1.179 $self->{nc}
4418 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4419     } else {
4420 wakaba 1.179 $self->{set_nc}->($self);
4421 wakaba 1.175 }
4422 wakaba 1.124
4423 wakaba 1.161 redo A;
4424 wakaba 1.124 } else {
4425    
4426 wakaba 1.179 $self->{ct}->{data} .= ']]'; # character
4427 wakaba 1.161 $self->{state} = CDATA_SECTION_STATE;
4428     ## Reconsume.
4429     redo A;
4430 wakaba 1.124 }
4431 wakaba 1.163 } elsif ($self->{state} == ENTITY_STATE) {
4432 wakaba 1.183 if ($is_space->{$self->{nc}} or
4433     {
4434     0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
4435     $self->{entity_add} => 1,
4436     }->{$self->{nc}}) {
4437 wakaba 1.164
4438     ## Don't consume
4439     ## No error
4440     ## Return nothing.
4441     #
4442 wakaba 1.179 } elsif ($self->{nc} == 0x0023) { # #
4443 wakaba 1.166
4444 wakaba 1.164 $self->{state} = ENTITY_HASH_STATE;
4445 wakaba 1.179 $self->{s_kwd} = '#';
4446 wakaba 1.164
4447 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4448     $self->{line_prev} = $self->{line};
4449     $self->{column_prev} = $self->{column};
4450     $self->{column}++;
4451 wakaba 1.179 $self->{nc}
4452 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4453     } else {
4454 wakaba 1.179 $self->{set_nc}->($self);
4455 wakaba 1.175 }
4456 wakaba 1.1
4457 wakaba 1.164 redo A;
4458 wakaba 1.179 } elsif ((0x0041 <= $self->{nc} and
4459     $self->{nc} <= 0x005A) or # A..Z
4460     (0x0061 <= $self->{nc} and
4461     $self->{nc} <= 0x007A)) { # a..z
4462 wakaba 1.166
4463 wakaba 1.164 require Whatpm::_NamedEntityList;
4464     $self->{state} = ENTITY_NAME_STATE;
4465 wakaba 1.179 $self->{s_kwd} = chr $self->{nc};
4466     $self->{entity__value} = $self->{s_kwd};
4467 wakaba 1.164 $self->{entity__match} = 0;
4468 wakaba 1.1
4469 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4470     $self->{line_prev} = $self->{line};
4471     $self->{column_prev} = $self->{column};
4472     $self->{column}++;
4473 wakaba 1.179 $self->{nc}
4474 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4475     } else {
4476 wakaba 1.179 $self->{set_nc}->($self);
4477 wakaba 1.175 }
4478 wakaba 1.1
4479 wakaba 1.164 redo A;
4480     } else {
4481    
4482     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare ero');
4483     ## Return nothing.
4484     #
4485     }
4486    
4487     ## NOTE: No character is consumed by the "consume a character
4488     ## reference" algorithm. In other word, there is an "&" character
4489     ## that does not introduce a character reference, which would be
4490     ## appended to the parent element or the attribute value in later
4491     ## process of the tokenizer.
4492    
4493 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4494 wakaba 1.166
4495 wakaba 1.165 $self->{state} = $self->{prev_state};
4496 wakaba 1.164 ## Reconsume.
4497     return ({type => CHARACTER_TOKEN, data => '&',
4498     line => $self->{line_prev},
4499     column => $self->{column_prev},
4500     });
4501     redo A;
4502 wakaba 1.165 } else {
4503 wakaba 1.166
4504 wakaba 1.179 $self->{ca}->{value} .= '&';
4505 wakaba 1.165 $self->{state} = $self->{prev_state};
4506     ## Reconsume.
4507     redo A;
4508 wakaba 1.164 }
4509     } elsif ($self->{state} == ENTITY_HASH_STATE) {
4510 wakaba 1.179 if ($self->{nc} == 0x0078 or # x
4511     $self->{nc} == 0x0058) { # X
4512 wakaba 1.166
4513 wakaba 1.164 $self->{state} = HEXREF_X_STATE;
4514 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
4515 wakaba 1.164
4516 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4517     $self->{line_prev} = $self->{line};
4518     $self->{column_prev} = $self->{column};
4519     $self->{column}++;
4520 wakaba 1.179 $self->{nc}
4521 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4522     } else {
4523 wakaba 1.179 $self->{set_nc}->($self);
4524 wakaba 1.175 }
4525 wakaba 1.1
4526 wakaba 1.163 redo A;
4527 wakaba 1.179 } elsif (0x0030 <= $self->{nc} and
4528     $self->{nc} <= 0x0039) { # 0..9
4529 wakaba 1.166
4530 wakaba 1.164 $self->{state} = NCR_NUM_STATE;
4531 wakaba 1.179 $self->{s_kwd} = $self->{nc} - 0x0030;
4532 wakaba 1.164
4533 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4534     $self->{line_prev} = $self->{line};
4535     $self->{column_prev} = $self->{column};
4536     $self->{column}++;
4537 wakaba 1.179 $self->{nc}
4538 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4539     } else {
4540 wakaba 1.179 $self->{set_nc}->($self);
4541 wakaba 1.175 }
4542 wakaba 1.1
4543 wakaba 1.164 redo A;
4544     } else {
4545     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare nero',
4546     line => $self->{line_prev},
4547     column => $self->{column_prev} - 1);
4548    
4549     ## NOTE: According to the spec algorithm, nothing is returned,
4550     ## and then "&#" is appended to the parent element or the attribute
4551     ## value in the later processing.
4552    
4553 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4554 wakaba 1.166
4555 wakaba 1.165 $self->{state} = $self->{prev_state};
4556 wakaba 1.164 ## Reconsume.
4557     return ({type => CHARACTER_TOKEN,
4558     data => '&#',
4559     line => $self->{line_prev},
4560     column => $self->{column_prev} - 1,
4561     });
4562     redo A;
4563 wakaba 1.165 } else {
4564 wakaba 1.166
4565 wakaba 1.179 $self->{ca}->{value} .= '&#';
4566 wakaba 1.165 $self->{state} = $self->{prev_state};
4567     ## Reconsume.
4568     redo A;
4569 wakaba 1.164 }
4570     }
4571     } elsif ($self->{state} == NCR_NUM_STATE) {
4572 wakaba 1.179 if (0x0030 <= $self->{nc} and
4573     $self->{nc} <= 0x0039) { # 0..9
4574 wakaba 1.78
4575 wakaba 1.179 $self->{s_kwd} *= 10;
4576     $self->{s_kwd} += $self->{nc} - 0x0030;
4577 wakaba 1.1
4578 wakaba 1.164 ## Stay in the state.
4579 wakaba 1.1
4580 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4581     $self->{line_prev} = $self->{line};
4582     $self->{column_prev} = $self->{column};
4583     $self->{column}++;
4584 wakaba 1.179 $self->{nc}
4585 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4586     } else {
4587 wakaba 1.179 $self->{set_nc}->($self);
4588 wakaba 1.175 }
4589 wakaba 1.1
4590 wakaba 1.164 redo A;
4591 wakaba 1.179 } elsif ($self->{nc} == 0x003B) { # ;
4592 wakaba 1.1
4593 wakaba 1.78
4594 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4595     $self->{line_prev} = $self->{line};
4596     $self->{column_prev} = $self->{column};
4597     $self->{column}++;
4598 wakaba 1.179 $self->{nc}
4599 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4600     } else {
4601 wakaba 1.179 $self->{set_nc}->($self);
4602 wakaba 1.175 }
4603 wakaba 1.1
4604 wakaba 1.164 #
4605 wakaba 1.1 } else {
4606 wakaba 1.78
4607 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc');
4608     ## Reconsume.
4609     #
4610 wakaba 1.1 }
4611    
4612 wakaba 1.179 my $code = $self->{s_kwd};
4613 wakaba 1.164 my $l = $self->{line_prev};
4614     my $c = $self->{column_prev};
4615 wakaba 1.187 if ($charref_map->{$code}) {
4616 wakaba 1.78
4617 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4618     text => (sprintf 'U+%04X', $code),
4619     line => $l, column => $c);
4620 wakaba 1.187 $code = $charref_map->{$code};
4621 wakaba 1.26 } elsif ($code > 0x10FFFF) {
4622 wakaba 1.78
4623 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4624     text => (sprintf 'U-%08X', $code),
4625     line => $l, column => $c);
4626 wakaba 1.26 $code = 0xFFFD;
4627 wakaba 1.1 }
4628 wakaba 1.164
4629 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4630 wakaba 1.166
4631 wakaba 1.165 $self->{state} = $self->{prev_state};
4632 wakaba 1.164 ## Reconsume.
4633 wakaba 1.165 return ({type => CHARACTER_TOKEN, data => chr $code,
4634     line => $l, column => $c,
4635     });
4636 wakaba 1.164 redo A;
4637     } else {
4638 wakaba 1.166
4639 wakaba 1.179 $self->{ca}->{value} .= chr $code;
4640     $self->{ca}->{has_reference} = 1;
4641 wakaba 1.165 $self->{state} = $self->{prev_state};
4642 wakaba 1.164 ## Reconsume.
4643     redo A;
4644     }
4645     } elsif ($self->{state} == HEXREF_X_STATE) {
4646 wakaba 1.179 if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
4647     (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
4648     (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
4649 wakaba 1.164 # 0..9, A..F, a..f
4650 wakaba 1.166
4651 wakaba 1.164 $self->{state} = HEXREF_HEX_STATE;
4652 wakaba 1.179 $self->{s_kwd} = 0;
4653 wakaba 1.164 ## Reconsume.
4654     redo A;
4655     } else {
4656     $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare hcro',
4657     line => $self->{line_prev},
4658     column => $self->{column_prev} - 2);
4659    
4660     ## NOTE: According to the spec algorithm, nothing is returned,
4661     ## and then "&#" followed by "X" or "x" is appended to the parent
4662     ## element or the attribute value in the later processing.
4663    
4664 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4665 wakaba 1.166
4666 wakaba 1.165 $self->{state} = $self->{prev_state};
4667 wakaba 1.164 ## Reconsume.
4668     return ({type => CHARACTER_TOKEN,
4669 wakaba 1.179 data => '&' . $self->{s_kwd},
4670 wakaba 1.164 line => $self->{line_prev},
4671 wakaba 1.179 column => $self->{column_prev} - length $self->{s_kwd},
4672 wakaba 1.164 });
4673     redo A;
4674 wakaba 1.165 } else {
4675 wakaba 1.166
4676 wakaba 1.179 $self->{ca}->{value} .= '&' . $self->{s_kwd};
4677 wakaba 1.165 $self->{state} = $self->{prev_state};
4678     ## Reconsume.
4679     redo A;
4680 wakaba 1.164 }
4681     }
4682     } elsif ($self->{state} == HEXREF_HEX_STATE) {
4683 wakaba 1.179 if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
4684 wakaba 1.164 # 0..9
4685    
4686 wakaba 1.179 $self->{s_kwd} *= 0x10;
4687     $self->{s_kwd} += $self->{nc} - 0x0030;
4688 wakaba 1.164 ## Stay in the state.
4689    
4690 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4691     $self->{line_prev} = $self->{line};
4692     $self->{column_prev} = $self->{column};
4693     $self->{column}++;
4694 wakaba 1.179 $self->{nc}
4695 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4696     } else {
4697 wakaba 1.179 $self->{set_nc}->($self);
4698 wakaba 1.175 }
4699 wakaba 1.164
4700     redo A;
4701 wakaba 1.179 } elsif (0x0061 <= $self->{nc} and
4702     $self->{nc} <= 0x0066) { # a..f
4703 wakaba 1.164
4704 wakaba 1.179 $self->{s_kwd} *= 0x10;
4705     $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
4706 wakaba 1.164 ## Stay in the state.
4707    
4708 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4709     $self->{line_prev} = $self->{line};
4710     $self->{column_prev} = $self->{column};
4711     $self->{column}++;
4712 wakaba 1.179 $self->{nc}
4713 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4714     } else {
4715 wakaba 1.179 $self->{set_nc}->($self);
4716 wakaba 1.175 }
4717 wakaba 1.1
4718 wakaba 1.164 redo A;
4719 wakaba 1.179 } elsif (0x0041 <= $self->{nc} and
4720     $self->{nc} <= 0x0046) { # A..F
4721 wakaba 1.164
4722 wakaba 1.179 $self->{s_kwd} *= 0x10;
4723     $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
4724 wakaba 1.164 ## Stay in the state.
4725    
4726 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4727     $self->{line_prev} = $self->{line};
4728     $self->{column_prev} = $self->{column};
4729     $self->{column}++;
4730 wakaba 1.179 $self->{nc}
4731 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4732     } else {
4733 wakaba 1.179 $self->{set_nc}->($self);
4734 wakaba 1.175 }
4735 wakaba 1.16
4736 wakaba 1.164 redo A;
4737 wakaba 1.179 } elsif ($self->{nc} == 0x003B) { # ;
4738 wakaba 1.164
4739    
4740 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4741     $self->{line_prev} = $self->{line};
4742     $self->{column_prev} = $self->{column};
4743     $self->{column}++;
4744 wakaba 1.179 $self->{nc}
4745 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4746     } else {
4747 wakaba 1.179 $self->{set_nc}->($self);
4748 wakaba 1.175 }
4749 wakaba 1.37
4750 wakaba 1.164 #
4751 wakaba 1.1 } else {
4752 wakaba 1.78
4753 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc',
4754     line => $self->{line},
4755     column => $self->{column});
4756     ## Reconsume.
4757     #
4758     }
4759    
4760 wakaba 1.179 my $code = $self->{s_kwd};
4761 wakaba 1.164 my $l = $self->{line_prev};
4762     my $c = $self->{column_prev};
4763 wakaba 1.187 if ($charref_map->{$code}) {
4764 wakaba 1.164
4765     $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4766     text => (sprintf 'U+%04X', $code),
4767     line => $l, column => $c);
4768 wakaba 1.187 $code = $charref_map->{$code};
4769 wakaba 1.164 } elsif ($code > 0x10FFFF) {
4770 wakaba 1.37
4771 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'invalid character reference',
4772     text => (sprintf 'U-%08X', $code),
4773     line => $l, column => $c);
4774     $code = 0xFFFD;
4775     }
4776    
4777 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4778 wakaba 1.166
4779 wakaba 1.165 $self->{state} = $self->{prev_state};
4780 wakaba 1.164 ## Reconsume.
4781 wakaba 1.165 return ({type => CHARACTER_TOKEN, data => chr $code,
4782     line => $l, column => $c,
4783     });
4784 wakaba 1.164 redo A;
4785     } else {
4786 wakaba 1.166
4787 wakaba 1.179 $self->{ca}->{value} .= chr $code;
4788     $self->{ca}->{has_reference} = 1;
4789 wakaba 1.165 $self->{state} = $self->{prev_state};
4790 wakaba 1.164 ## Reconsume.
4791     redo A;
4792     }
4793     } elsif ($self->{state} == ENTITY_NAME_STATE) {
4794 wakaba 1.179 if (length $self->{s_kwd} < 30 and
4795 wakaba 1.164 ## NOTE: Some number greater than the maximum length of entity name
4796 wakaba 1.179 ((0x0041 <= $self->{nc} and # a
4797     $self->{nc} <= 0x005A) or # x
4798     (0x0061 <= $self->{nc} and # a
4799     $self->{nc} <= 0x007A) or # z
4800     (0x0030 <= $self->{nc} and # 0
4801     $self->{nc} <= 0x0039) or # 9
4802     $self->{nc} == 0x003B)) { # ;
4803 wakaba 1.164 our $EntityChar;
4804 wakaba 1.179 $self->{s_kwd} .= chr $self->{nc};
4805     if (defined $EntityChar->{$self->{s_kwd}}) {
4806     if ($self->{nc} == 0x003B) { # ;
4807 wakaba 1.164
4808 wakaba 1.179 $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
4809 wakaba 1.164 $self->{entity__match} = 1;
4810    
4811 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4812     $self->{line_prev} = $self->{line};
4813     $self->{column_prev} = $self->{column};
4814     $self->{column}++;
4815 wakaba 1.179 $self->{nc}
4816 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4817     } else {
4818 wakaba 1.179 $self->{set_nc}->($self);
4819 wakaba 1.175 }
4820 wakaba 1.1
4821 wakaba 1.164 #
4822     } else {
4823    
4824 wakaba 1.179 $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
4825 wakaba 1.164 $self->{entity__match} = -1;
4826     ## Stay in the state.
4827    
4828 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4829     $self->{line_prev} = $self->{line};
4830     $self->{column_prev} = $self->{column};
4831     $self->{column}++;
4832 wakaba 1.179 $self->{nc}
4833 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4834     } else {
4835 wakaba 1.179 $self->{set_nc}->($self);
4836 wakaba 1.175 }
4837 wakaba 1.164
4838     redo A;
4839     }
4840     } else {
4841    
4842 wakaba 1.179 $self->{entity__value} .= chr $self->{nc};
4843 wakaba 1.164 $self->{entity__match} *= 2;
4844     ## Stay in the state.
4845    
4846 wakaba 1.175 if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
4847     $self->{line_prev} = $self->{line};
4848     $self->{column_prev} = $self->{column};
4849     $self->{column}++;
4850 wakaba 1.179 $self->{nc}
4851 wakaba 1.175 = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
4852     } else {
4853 wakaba 1.179 $self->{set_nc}->($self);
4854 wakaba 1.175 }
4855 wakaba 1.164
4856     redo A;
4857     }
4858     }
4859    
4860     my $data;
4861     my $has_ref;
4862     if ($self->{entity__match} > 0) {
4863    
4864     $data = $self->{entity__value};
4865     $has_ref = 1;
4866     #
4867     } elsif ($self->{entity__match} < 0) {
4868     $self->{parse_error}->(level => $self->{level}->{must}, type => 'no refc');
4869 wakaba 1.165 if ($self->{prev_state} != DATA_STATE and # in attribute
4870     $self->{entity__match} < -1) {
4871 wakaba 1.164
4872 wakaba 1.179 $data = '&' . $self->{s_kwd};
4873 wakaba 1.164 #
4874     } else {
4875    
4876     $data = $self->{entity__value};
4877     $has_ref = 1;
4878     #
4879     }
4880     } else {
4881 wakaba 1.78
4882 wakaba 1.164 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bare ero',
4883     line => $self->{line_prev},
4884 wakaba 1.179 column => $self->{column_prev} - length $self->{s_kwd});
4885     $data = '&' . $self->{s_kwd};
4886 wakaba 1.164 #
4887     }
4888    
4889     ## NOTE: In these cases, when a character reference is found,
4890     ## it is consumed and a character token is returned, or, otherwise,
4891     ## nothing is consumed and returned, according to the spec algorithm.
4892     ## In this implementation, anything that has been examined by the
4893     ## tokenizer is appended to the parent element or the attribute value
4894     ## as string, either literal string when no character reference or
4895     ## entity-replaced string otherwise, in this stage, since any characters
4896     ## that would not be consumed are appended in the data state or in an
4897     ## appropriate attribute value state anyway.
4898    
4899 wakaba 1.165 if ($self->{prev_state} == DATA_STATE) {
4900 wakaba 1.166
4901 wakaba 1.165 $self->{state} = $self->{prev_state};
4902 wakaba 1.164 ## Reconsume.
4903     return ({type => CHARACTER_TOKEN,
4904 wakaba 1.165 data => $data,
4905 wakaba 1.164 line => $self->{line_prev},
4906 wakaba 1.179 column => $self->{column_prev} + 1 - length $self->{s_kwd},
4907 wakaba 1.164 });
4908 wakaba 1.163 redo A;
4909 wakaba 1.165 } else {
4910 wakaba 1.166
4911 wakaba 1.179 $self->{ca}->{value} .= $data;
4912     $self->{ca}->{has_reference} = 1 if $has_ref;
4913 wakaba 1.165 $self->{state} = $self->{prev_state};
4914     ## Reconsume.
4915     redo A;
4916 wakaba 1.37 }
4917 wakaba 1.1 } else {
4918 wakaba 1.163 die "$0: $self->{state}: Unknown state";
4919     }
4920     } # A
4921    
4922     die "$0: _get_next_token: unexpected case";
4923     } # _get_next_token
4924 wakaba 1.1
4925     sub _initialize_tree_constructor ($) {
4926     my $self = shift;
4927     ## NOTE: $self->{document} MUST be specified before this method is called
4928     $self->{document}->strict_error_checking (0);
4929     ## TODO: Turn mutation events off # MUST
4930     ## TODO: Turn loose Document option (manakai extension) on
4931 wakaba 1.18 $self->{document}->manakai_is_html (1); # MUST
4932 wakaba 1.151 $self->{document}->set_user_data (manakai_source_line => 1);
4933     $self->{document}->set_user_data (manakai_source_column => 1);
4934 wakaba 1.1 } # _initialize_tree_constructor
4935    
4936     sub _terminate_tree_constructor ($) {
4937     my $self = shift;
4938     $self->{document}->strict_error_checking (1);
4939     ## TODO: Turn mutation events on
4940     } # _terminate_tree_constructor
4941    
4942     ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?
4943    
4944 wakaba 1.3 { # tree construction stage
4945     my $token;
4946    
4947 wakaba 1.1 sub _construct_tree ($) {
4948     my ($self) = @_;
4949    
4950     ## When an interactive UA render the $self->{document} available
4951     ## to the user, or when it begin accepting user input, are
4952     ## not defined.
4953    
4954     $token = $self->_get_next_token;
4955    
4956 wakaba 1.3 undef $self->{form_element};
4957     undef $self->{head_element};
4958 wakaba 1.197 undef $self->{head_element_inserted};
4959 wakaba 1.3 $self->{open_elements} = [];
4960     undef $self->{inner_html_node};
4961    
4962 wakaba 1.84 ## NOTE: The "initial" insertion mode.
4963 wakaba 1.3 $self->_tree_construction_initial; # MUST
4964 wakaba 1.84
4965 wakaba 1.85 ## NOTE: The "before html" insertion mode.
4966 wakaba 1.3 $self->_tree_construction_root_element;
4967 wakaba 1.84 $self->{insertion_mode} = BEFORE_HEAD_IM;
4968    
4969     ## NOTE: The "before head" insertion mode and so on.
4970 wakaba 1.3 $self->_tree_construction_main;
4971     } # _construct_tree
4972    
4973     sub _tree_construction_initial ($) {
4974     my $self = shift;
4975 wakaba 1.84
4976     ## NOTE: "initial" insertion mode
4977    
4978 wakaba 1.18 INITIAL: {
4979 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
4980 wakaba 1.18 ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
4981     ## error, switch to a conformance checking mode for another
4982     ## language.
4983     my $doctype_name = $token->{name};
4984     $doctype_name = '' unless defined $doctype_name;
4985 wakaba 1.155 $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
4986 wakaba 1.18 if (not defined $token->{name} or # <!DOCTYPE>
4987 wakaba 1.179 defined $token->{sysid}) {
4988 wakaba 1.79
4989 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
4990 wakaba 1.18 } elsif ($doctype_name ne 'HTML') {
4991 wakaba 1.79
4992 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
4993 wakaba 1.179 } elsif (defined $token->{pubid}) {
4994     if ($token->{pubid} eq 'XSLT-compat') {
4995 wakaba 1.155
4996     $self->{parse_error}->(level => $self->{level}->{must}, type => 'XSLT-compat', token => $token,
4997     level => $self->{level}->{should});
4998     } else {
4999     $self->{parse_error}->(level => $self->{level}->{must}, type => 'not HTML5', token => $token);
5000     }
5001 wakaba 1.79 } else {
5002    
5003 wakaba 1.155 #
5004 wakaba 1.18 }
5005    
5006     my $doctype = $self->{document}->create_document_type_definition
5007     ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
5008 wakaba 1.120 ## NOTE: Default value for both |public_id| and |system_id| attributes
5009     ## are empty strings, so that we don't set any value in missing cases.
5010 wakaba 1.179 $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
5011     $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
5012 wakaba 1.18 ## NOTE: Other DocumentType attributes are null or empty lists.
5013     ## ISSUE: internalSubset = null??
5014     $self->{document}->append_child ($doctype);
5015    
5016 wakaba 1.75 if ($token->{quirks} or $doctype_name ne 'HTML') {
5017 wakaba 1.79
5018 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5019 wakaba 1.179 } elsif (defined $token->{pubid}) {
5020     my $pubid = $token->{pubid};
5021 wakaba 1.18 $pubid =~ tr/a-z/A-z/;
5022 wakaba 1.140 my $prefix = [
5023     "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
5024     "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
5025     "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
5026     "-//IETF//DTD HTML 2.0 LEVEL 1//",
5027     "-//IETF//DTD HTML 2.0 LEVEL 2//",
5028     "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
5029     "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
5030     "-//IETF//DTD HTML 2.0 STRICT//",
5031     "-//IETF//DTD HTML 2.0//",
5032     "-//IETF//DTD HTML 2.1E//",
5033     "-//IETF//DTD HTML 3.0//",
5034     "-//IETF//DTD HTML 3.2 FINAL//",
5035     "-//IETF//DTD HTML 3.2//",
5036     "-//IETF//DTD HTML 3//",
5037     "-//IETF//DTD HTML LEVEL 0//",
5038     "-//IETF//DTD HTML LEVEL 1//",
5039     "-//IETF//DTD HTML LEVEL 2//",
5040     "-//IETF//DTD HTML LEVEL 3//",
5041     "-//IETF//DTD HTML STRICT LEVEL 0//",
5042     "-//IETF//DTD HTML STRICT LEVEL 1//",
5043     "-//IETF//DTD HTML STRICT LEVEL 2//",
5044     "-//IETF//DTD HTML STRICT LEVEL 3//",
5045     "-//IETF//DTD HTML STRICT//",
5046     "-//IETF//DTD HTML//",
5047     "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
5048     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
5049     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
5050     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
5051     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
5052     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
5053     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
5054     "-//NETSCAPE COMM. CORP.//DTD HTML//",
5055     "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
5056     "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
5057     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
5058     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
5059     "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
5060     "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
5061     "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
5062     "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
5063     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
5064     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
5065     "-//W3C//DTD HTML 3 1995-03-24//",
5066     "-//W3C//DTD HTML 3.2 DRAFT//",
5067     "-//W3C//DTD HTML 3.2 FINAL//",
5068     "-//W3C//DTD HTML 3.2//",
5069     "-//W3C//DTD HTML 3.2S DRAFT//",
5070     "-//W3C//DTD HTML 4.0 FRAMESET//",
5071     "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
5072     "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
5073     "-//W3C//DTD HTML EXPERIMENTAL 970421//",
5074     "-//W3C//DTD W3 HTML//",
5075     "-//W3O//DTD W3 HTML 3.0//",
5076     "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
5077     "-//WEBTECHS//DTD MOZILLA HTML//",
5078     ]; # $prefix
5079     my $match;
5080     for (@$prefix) {
5081     if (substr ($prefix, 0, length $_) eq $_) {
5082     $match = 1;
5083     last;
5084     }
5085     }
5086     if ($match or
5087     $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
5088     $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
5089     $pubid eq "HTML") {
5090 wakaba 1.79
5091 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5092 wakaba 1.140 } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
5093     $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
5094 wakaba 1.179 if (defined $token->{sysid}) {
5095 wakaba 1.79
5096 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5097     } else {
5098 wakaba 1.79
5099 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
5100 wakaba 1.3 }
5101 wakaba 1.140 } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
5102     $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
5103 wakaba 1.79
5104 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
5105 wakaba 1.79 } else {
5106    
5107 wakaba 1.18 }
5108 wakaba 1.79 } else {
5109    
5110 wakaba 1.18 }
5111 wakaba 1.179 if (defined $token->{sysid}) {
5112     my $sysid = $token->{sysid};
5113 wakaba 1.18 $sysid =~ tr/A-Z/a-z/;
5114     if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
5115 wakaba 1.140 ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
5116     ## marked as quirks.
5117 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5118 wakaba 1.79
5119     } else {
5120    
5121 wakaba 1.18 }
5122 wakaba 1.79 } else {
5123    
5124 wakaba 1.18 }
5125    
5126 wakaba 1.85 ## Go to the "before html" insertion mode.
5127 wakaba 1.18 $token = $self->_get_next_token;
5128     return;
5129     } elsif ({
5130 wakaba 1.57 START_TAG_TOKEN, 1,
5131     END_TAG_TOKEN, 1,
5132     END_OF_FILE_TOKEN, 1,
5133 wakaba 1.18 }->{$token->{type}}) {
5134 wakaba 1.79
5135 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE', token => $token);
5136 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5137 wakaba 1.85 ## Go to the "before html" insertion mode.
5138 wakaba 1.18 ## reprocess
5139 wakaba 1.122
5140 wakaba 1.18 return;
5141 wakaba 1.57 } elsif ($token->{type} == CHARACTER_TOKEN) {
5142 wakaba 1.184 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5143 wakaba 1.18 ## Ignore the token
5144 wakaba 1.26
5145 wakaba 1.18 unless (length $token->{data}) {
5146 wakaba 1.79
5147 wakaba 1.84 ## Stay in the insertion mode.
5148 wakaba 1.18 $token = $self->_get_next_token;
5149     redo INITIAL;
5150 wakaba 1.79 } else {
5151    
5152 wakaba 1.3 }
5153 wakaba 1.79 } else {
5154    
5155 wakaba 1.3 }
5156 wakaba 1.18
5157 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'no DOCTYPE', token => $token);
5158 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
5159 wakaba 1.85 ## Go to the "before html" insertion mode.
5160 wakaba 1.18 ## reprocess
5161     return;
5162 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
5163 wakaba 1.79
5164 wakaba 1.18 my $comment = $self->{document}->create_comment ($token->{data});
5165     $self->{document}->append_child ($comment);
5166    
5167 wakaba 1.84 ## Stay in the insertion mode.
5168 wakaba 1.18 $token = $self->_get_next_token;
5169     redo INITIAL;
5170     } else {
5171 wakaba 1.57 die "$0: $token->{type}: Unknown token type";
5172 wakaba 1.18 }
5173     } # INITIAL
5174 wakaba 1.79
5175     die "$0: _tree_construction_initial: This should be never reached";
5176 wakaba 1.3 } # _tree_construction_initial
5177    
5178     sub _tree_construction_root_element ($) {
5179     my $self = shift;
5180 wakaba 1.84
5181 wakaba 1.85 ## NOTE: "before html" insertion mode.
5182 wakaba 1.3
5183     B: {
5184 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
5185 wakaba 1.79
5186 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in html:#DOCTYPE', token => $token);
5187 wakaba 1.3 ## Ignore the token
5188 wakaba 1.84 ## Stay in the insertion mode.
5189 wakaba 1.3 $token = $self->_get_next_token;
5190     redo B;
5191 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
5192 wakaba 1.79
5193 wakaba 1.3 my $comment = $self->{document}->create_comment ($token->{data});
5194     $self->{document}->append_child ($comment);
5195 wakaba 1.84 ## Stay in the insertion mode.
5196 wakaba 1.3 $token = $self->_get_next_token;
5197     redo B;
5198 wakaba 1.57 } elsif ($token->{type} == CHARACTER_TOKEN) {
5199 wakaba 1.184 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5200 wakaba 1.26 ## Ignore the token.
5201    
5202 wakaba 1.3 unless (length $token->{data}) {
5203 wakaba 1.79
5204 wakaba 1.84 ## Stay in the insertion mode.
5205 wakaba 1.3 $token = $self->_get_next_token;
5206     redo B;
5207 wakaba 1.79 } else {
5208    
5209 wakaba 1.3 }
5210 wakaba 1.79 } else {
5211    
5212 wakaba 1.3 }
5213 wakaba 1.63
5214     $self->{application_cache_selection}->(undef);
5215    
5216     #
5217     } elsif ($token->{type} == START_TAG_TOKEN) {
5218 wakaba 1.84 if ($token->{tag_name} eq 'html') {
5219     my $root_element;
5220 wakaba 1.79
5221 wakaba 1.84 $root_element = $self->{document}->create_element_ns
5222 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5223 wakaba 1.84
5224     for my $attr_name (keys %{ $token->{attributes}}) {
5225 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5226 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5227 wakaba 1.117 $attr->value ($attr_t->{value});
5228     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5229     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5230     $root_element->set_attribute_node_ns ($attr);
5231 wakaba 1.84 }
5232    
5233 wakaba 1.114 $root_element->set_user_data (manakai_source_line => $token->{line})
5234     if defined $token->{line};
5235     $root_element->set_user_data (manakai_source_column => $token->{column})
5236     if defined $token->{column};
5237    
5238 wakaba 1.84 $self->{document}->append_child ($root_element);
5239 wakaba 1.121 push @{$self->{open_elements}},
5240     [$root_element, $el_category->{html}];
5241 wakaba 1.84
5242     if ($token->{attributes}->{manifest}) {
5243    
5244     $self->{application_cache_selection}
5245     ->($token->{attributes}->{manifest}->{value});
5246 wakaba 1.116 ## ISSUE: Spec is unclear on relative references.
5247     ## According to Hixie (#whatwg 2008-03-19), it should be
5248     ## resolved against the base URI of the document in HTML
5249     ## or xml:base of the element in XHTML.
5250 wakaba 1.84 } else {
5251    
5252     $self->{application_cache_selection}->(undef);
5253     }
5254    
5255 wakaba 1.122
5256    
5257 wakaba 1.84 $token = $self->_get_next_token;
5258     return; ## Go to the "before head" insertion mode.
5259 wakaba 1.63 } else {
5260 wakaba 1.79
5261 wakaba 1.84 #
5262 wakaba 1.63 }
5263 wakaba 1.3 } elsif ({
5264 wakaba 1.57 END_TAG_TOKEN, 1,
5265     END_OF_FILE_TOKEN, 1,
5266 wakaba 1.3 }->{$token->{type}}) {
5267 wakaba 1.79
5268 wakaba 1.3 #
5269     } else {
5270 wakaba 1.57 die "$0: $token->{type}: Unknown token type";
5271 wakaba 1.3 }
5272 wakaba 1.63
5273 wakaba 1.123 my $root_element;
5274    
5275 wakaba 1.3 $root_element = $self->{document}->create_element_ns
5276 wakaba 1.128 ($HTML_NS, [undef, 'html']);
5277 wakaba 1.3
5278 wakaba 1.114 $root_element->set_user_data (manakai_source_line => $token->{line})
5279     if defined $token->{line};
5280     $root_element->set_user_data (manakai_source_column => $token->{column})
5281     if defined $token->{column};
5282    
5283 wakaba 1.84 $self->{document}->append_child ($root_element);
5284 wakaba 1.121 push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
5285 wakaba 1.84
5286     $self->{application_cache_selection}->(undef);
5287    
5288     ## NOTE: Reprocess the token.
5289 wakaba 1.122
5290 wakaba 1.84 return; ## Go to the "before head" insertion mode.
5291 wakaba 1.3 } # B
5292 wakaba 1.79
5293     die "$0: _tree_construction_root_element: This should never be reached";
5294 wakaba 1.3 } # _tree_construction_root_element
5295    
5296     sub _reset_insertion_mode ($) {
5297     my $self = shift;
5298    
5299     ## Step 1
5300     my $last;
5301    
5302     ## Step 2
5303     my $i = -1;
5304     my $node = $self->{open_elements}->[$i];
5305    
5306     ## Step 3
5307     S3: {
5308 wakaba 1.29 if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
5309     $last = 1;
5310     if (defined $self->{inner_html_node}) {
5311 wakaba 1.137
5312     $node = $self->{inner_html_node};
5313     } else {
5314     die "_reset_insertion_mode: t27";
5315 wakaba 1.3 }
5316     }
5317 wakaba 1.137
5318     ## Step 4..14
5319     my $new_mode;
5320     if ($node->[1] & FOREIGN_EL) {
5321    
5322     ## NOTE: Strictly spaking, the line below only applies to MathML and
5323     ## SVG elements. Currently the HTML syntax supports only MathML and
5324     ## SVG elements as foreigners.
5325 wakaba 1.145 $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
5326 wakaba 1.137 } elsif ($node->[1] & TABLE_CELL_EL) {
5327     if ($last) {
5328    
5329     #
5330     } else {
5331    
5332     $new_mode = IN_CELL_IM;
5333     }
5334     } else {
5335    
5336     $new_mode = {
5337 wakaba 1.56 select => IN_SELECT_IM,
5338 wakaba 1.83 ## NOTE: |option| and |optgroup| do not set
5339     ## insertion mode to "in select" by themselves.
5340 wakaba 1.56 tr => IN_ROW_IM,
5341     tbody => IN_TABLE_BODY_IM,
5342     thead => IN_TABLE_BODY_IM,
5343     tfoot => IN_TABLE_BODY_IM,
5344     caption => IN_CAPTION_IM,
5345     colgroup => IN_COLUMN_GROUP_IM,
5346     table => IN_TABLE_IM,
5347     head => IN_BODY_IM, # not in head!
5348     body => IN_BODY_IM,
5349     frameset => IN_FRAMESET_IM,
5350 wakaba 1.121 }->{$node->[0]->manakai_local_name};
5351 wakaba 1.137 }
5352     $self->{insertion_mode} = $new_mode and return if defined $new_mode;
5353 wakaba 1.3
5354 wakaba 1.123 ## Step 15
5355 wakaba 1.121 if ($node->[1] & HTML_EL) {
5356 wakaba 1.3 unless (defined $self->{head_element}) {
5357 wakaba 1.79
5358 wakaba 1.56 $self->{insertion_mode} = BEFORE_HEAD_IM;
5359 wakaba 1.3 } else {
5360 wakaba 1.81 ## ISSUE: Can this state be reached?
5361 wakaba 1.79
5362 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
5363 wakaba 1.3 }
5364     return;
5365 wakaba 1.79 } else {
5366    
5367 wakaba 1.3 }
5368    
5369 wakaba 1.123 ## Step 16
5370 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM and return if $last;
5371 wakaba 1.3
5372 wakaba 1.123 ## Step 17
5373 wakaba 1.3 $i--;
5374     $node = $self->{open_elements}->[$i];
5375    
5376 wakaba 1.123 ## Step 18
5377 wakaba 1.3 redo S3;
5378     } # S3
5379 wakaba 1.79
5380     die "$0: _reset_insertion_mode: This line should never be reached";
5381 wakaba 1.3 } # _reset_insertion_mode
5382    
5383     sub _tree_construction_main ($) {
5384     my $self = shift;
5385    
5386 wakaba 1.1 my $active_formatting_elements = [];
5387    
5388     my $reconstruct_active_formatting_elements = sub { # MUST
5389     my $insert = shift;
5390    
5391     ## Step 1
5392     return unless @$active_formatting_elements;
5393    
5394     ## Step 3
5395     my $i = -1;
5396     my $entry = $active_formatting_elements->[$i];
5397    
5398     ## Step 2
5399     return if $entry->[0] eq '#marker';
5400 wakaba 1.3 for (@{$self->{open_elements}}) {
5401 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
5402 wakaba 1.79
5403 wakaba 1.1 return;
5404     }
5405     }
5406    
5407     S4: {
5408     ## Step 4
5409     last S4 if $active_formatting_elements->[0]->[0] eq $entry->[0];
5410    
5411     ## Step 5
5412     $i--;
5413     $entry = $active_formatting_elements->[$i];
5414    
5415     ## Step 6
5416     if ($entry->[0] eq '#marker') {
5417 wakaba 1.79
5418 wakaba 1.1 #
5419     } else {
5420     my $in_open_elements;
5421 wakaba 1.3 OE: for (@{$self->{open_elements}}) {
5422 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
5423 wakaba 1.79
5424 wakaba 1.1 $in_open_elements = 1;
5425     last OE;
5426     }
5427     }
5428     if ($in_open_elements) {
5429 wakaba 1.79
5430 wakaba 1.1 #
5431     } else {
5432 wakaba 1.81 ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
5433 wakaba 1.79
5434 wakaba 1.1 redo S4;
5435     }
5436     }
5437    
5438     ## Step 7
5439     $i++;
5440     $entry = $active_formatting_elements->[$i];
5441     } # S4
5442    
5443     S7: {
5444     ## Step 8
5445     my $clone = [$entry->[0]->clone_node (0), $entry->[1]];
5446    
5447     ## Step 9
5448     $insert->($clone->[0]);
5449 wakaba 1.3 push @{$self->{open_elements}}, $clone;
5450 wakaba 1.1
5451     ## Step 10
5452 wakaba 1.3 $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
5453 wakaba 1.1
5454     ## Step 11
5455     unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
5456 wakaba 1.79
5457 wakaba 1.1 ## Step 7'
5458     $i++;
5459     $entry = $active_formatting_elements->[$i];
5460    
5461     redo S7;
5462     }
5463 wakaba 1.79
5464    
5465 wakaba 1.1 } # S7
5466     }; # $reconstruct_active_formatting_elements
5467    
5468     my $clear_up_to_marker = sub {
5469     for (reverse 0..$#$active_formatting_elements) {
5470     if ($active_formatting_elements->[$_]->[0] eq '#marker') {
5471 wakaba 1.79
5472 wakaba 1.1 splice @$active_formatting_elements, $_;
5473     return;
5474     }
5475     }
5476 wakaba 1.79
5477    
5478 wakaba 1.1 }; # $clear_up_to_marker
5479    
5480 wakaba 1.96 my $insert;
5481    
5482     my $parse_rcdata = sub ($) {
5483     my ($content_model_flag) = @_;
5484 wakaba 1.25
5485     ## Step 1
5486     my $start_tag_name = $token->{tag_name};
5487    
5488 wakaba 1.200 {
5489     my $el;
5490    
5491 wakaba 1.25 $el = $self->{document}->create_element_ns
5492 wakaba 1.200 ($HTML_NS, [undef, $token->{tag_name}]);
5493 wakaba 1.1
5494 wakaba 1.200 for my $attr_name (keys %{ $token->{attributes}}) {
5495     my $attr_t = $token->{attributes}->{$attr_name};
5496 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5497 wakaba 1.117 $attr->value ($attr_t->{value});
5498     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5499     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5500     $el->set_attribute_node_ns ($attr);
5501 wakaba 1.6 }
5502    
5503 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5504     if defined $token->{line};
5505     $el->set_user_data (manakai_source_column => $token->{column})
5506     if defined $token->{column};
5507    
5508 wakaba 1.200 $insert->($el);
5509     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
5510     }
5511    
5512 wakaba 1.25
5513     ## Step 2
5514 wakaba 1.41 $self->{content_model} = $content_model_flag; # CDATA or RCDATA
5515 wakaba 1.13 delete $self->{escape}; # MUST
5516 wakaba 1.25
5517 wakaba 1.200 ## Step 3, 4
5518     $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
5519    
5520 wakaba 1.122
5521 wakaba 1.1 $token = $self->_get_next_token;
5522 wakaba 1.25 }; # $parse_rcdata
5523 wakaba 1.1
5524 wakaba 1.96 my $script_start_tag = sub () {
5525 wakaba 1.200 ## Step 1
5526 wakaba 1.1 my $script_el;
5527    
5528     $script_el = $self->{document}->create_element_ns
5529 wakaba 1.128 ($HTML_NS, [undef, 'script']);
5530 wakaba 1.1
5531     for my $attr_name (keys %{ $token->{attributes}}) {
5532 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5533 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5534 wakaba 1.117 $attr->value ($attr_t->{value});
5535     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5536     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5537     $script_el->set_attribute_node_ns ($attr);
5538 wakaba 1.1 }
5539    
5540 wakaba 1.114 $script_el->set_user_data (manakai_source_line => $token->{line})
5541     if defined $token->{line};
5542     $script_el->set_user_data (manakai_source_column => $token->{column})
5543     if defined $token->{column};
5544    
5545 wakaba 1.200
5546     ## Step 2
5547 wakaba 1.1 ## TODO: mark as "parser-inserted"
5548    
5549 wakaba 1.200 ## Step 3
5550     ## TODO: Mark as "already executed", if ...
5551    
5552     ## Step 4
5553     $insert->($script_el);
5554    
5555     ## ISSUE: $script_el is not put into the stack
5556     push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
5557    
5558     ## Step 5
5559 wakaba 1.41 $self->{content_model} = CDATA_CONTENT_MODEL;
5560 wakaba 1.13 delete $self->{escape}; # MUST
5561 wakaba 1.1
5562 wakaba 1.200 ## Step 6-7
5563     $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
5564 wakaba 1.25
5565 wakaba 1.1
5566     $token = $self->_get_next_token;
5567     }; # $script_start_tag
5568    
5569 wakaba 1.102 ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
5570     ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
5571 wakaba 1.197 ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
5572 wakaba 1.102 my $open_tables = [[$self->{open_elements}->[0]->[0]]];
5573    
5574 wakaba 1.1 my $formatting_end_tag = sub {
5575 wakaba 1.111 my $end_tag_token = shift;
5576     my $tag_name = $end_tag_token->{tag_name};
5577 wakaba 1.1
5578 wakaba 1.102 ## NOTE: The adoption agency algorithm (AAA).
5579    
5580 wakaba 1.1 FET: {
5581     ## Step 1
5582     my $formatting_element;
5583     my $formatting_element_i_in_active;
5584     AFE: for (reverse 0..$#$active_formatting_elements) {
5585 wakaba 1.121 if ($active_formatting_elements->[$_]->[0] eq '#marker') {
5586    
5587     last AFE;
5588     } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
5589     eq $tag_name) {
5590 wakaba 1.79
5591 wakaba 1.1 $formatting_element = $active_formatting_elements->[$_];
5592     $formatting_element_i_in_active = $_;
5593     last AFE;
5594     }
5595     } # AFE
5596     unless (defined $formatting_element) {
5597 wakaba 1.79
5598 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
5599 wakaba 1.1 ## Ignore the token
5600     $token = $self->_get_next_token;
5601     return;
5602     }
5603     ## has an element in scope
5604     my $in_scope = 1;
5605     my $formatting_element_i_in_open;
5606 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5607     my $node = $self->{open_elements}->[$_];
5608 wakaba 1.1 if ($node->[0] eq $formatting_element->[0]) {
5609     if ($in_scope) {
5610 wakaba 1.79
5611 wakaba 1.1 $formatting_element_i_in_open = $_;
5612     last INSCOPE;
5613     } else { # in open elements but not in scope
5614 wakaba 1.79
5615 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
5616     text => $token->{tag_name},
5617 wakaba 1.111 token => $end_tag_token);
5618 wakaba 1.1 ## Ignore the token
5619     $token = $self->_get_next_token;
5620     return;
5621     }
5622 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
5623 wakaba 1.79
5624 wakaba 1.1 $in_scope = 0;
5625     }
5626     } # INSCOPE
5627     unless (defined $formatting_element_i_in_open) {
5628 wakaba 1.79
5629 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
5630     text => $token->{tag_name},
5631 wakaba 1.111 token => $end_tag_token);
5632 wakaba 1.1 pop @$active_formatting_elements; # $formatting_element
5633     $token = $self->_get_next_token; ## TODO: ok?
5634     return;
5635     }
5636 wakaba 1.3 if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
5637 wakaba 1.79
5638 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
5639     text => $self->{open_elements}->[-1]->[0]
5640 wakaba 1.120 ->manakai_local_name,
5641 wakaba 1.111 token => $end_tag_token);
5642 wakaba 1.1 }
5643    
5644     ## Step 2
5645     my $furthest_block;
5646     my $furthest_block_i_in_open;
5647 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
5648     my $node = $self->{open_elements}->[$_];
5649 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
5650 wakaba 1.1 #not $phrasing_category->{$node->[1]} and
5651 wakaba 1.121 ($node->[1] & SPECIAL_EL or
5652     $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
5653 wakaba 1.79
5654 wakaba 1.1 $furthest_block = $node;
5655     $furthest_block_i_in_open = $_;
5656 wakaba 1.198 ## NOTE: The topmost (eldest) node.
5657 wakaba 1.1 } elsif ($node->[0] eq $formatting_element->[0]) {
5658 wakaba 1.79
5659 wakaba 1.1 last OE;
5660     }
5661     } # OE
5662    
5663     ## Step 3
5664     unless (defined $furthest_block) { # MUST
5665 wakaba 1.79
5666 wakaba 1.3 splice @{$self->{open_elements}}, $formatting_element_i_in_open;
5667 wakaba 1.1 splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
5668     $token = $self->_get_next_token;
5669     return;
5670     }
5671    
5672     ## Step 4
5673 wakaba 1.3 my $common_ancestor_node = $self->{open_elements}->[$formatting_element_i_in_open - 1];
5674 wakaba 1.1
5675     ## Step 5
5676     my $furthest_block_parent = $furthest_block->[0]->parent_node;
5677     if (defined $furthest_block_parent) {
5678 wakaba 1.79
5679 wakaba 1.1 $furthest_block_parent->remove_child ($furthest_block->[0]);
5680     }
5681    
5682     ## Step 6
5683     my $bookmark_prev_el
5684     = $active_formatting_elements->[$formatting_element_i_in_active - 1]
5685     ->[0];
5686    
5687     ## Step 7
5688     my $node = $furthest_block;
5689     my $node_i_in_open = $furthest_block_i_in_open;
5690     my $last_node = $furthest_block;
5691     S7: {
5692     ## Step 1
5693     $node_i_in_open--;
5694 wakaba 1.3 $node = $self->{open_elements}->[$node_i_in_open];
5695 wakaba 1.1
5696     ## Step 2
5697     my $node_i_in_active;
5698     S7S2: {
5699     for (reverse 0..$#$active_formatting_elements) {
5700     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
5701 wakaba 1.79
5702 wakaba 1.1 $node_i_in_active = $_;
5703     last S7S2;
5704     }
5705     }
5706 wakaba 1.3 splice @{$self->{open_elements}}, $node_i_in_open, 1;
5707 wakaba 1.1 redo S7;
5708     } # S7S2
5709    
5710     ## Step 3
5711     last S7 if $node->[0] eq $formatting_element->[0];
5712    
5713     ## Step 4
5714     if ($last_node->[0] eq $furthest_block->[0]) {
5715 wakaba 1.79
5716 wakaba 1.1 $bookmark_prev_el = $node->[0];
5717     }
5718    
5719     ## Step 5
5720     if ($node->[0]->has_child_nodes ()) {
5721 wakaba 1.79
5722 wakaba 1.1 my $clone = [$node->[0]->clone_node (0), $node->[1]];
5723     $active_formatting_elements->[$node_i_in_active] = $clone;
5724 wakaba 1.3 $self->{open_elements}->[$node_i_in_open] = $clone;
5725 wakaba 1.1 $node = $clone;
5726     }
5727    
5728     ## Step 6
5729     $node->[0]->append_child ($last_node->[0]);
5730    
5731     ## Step 7
5732     $last_node = $node;
5733    
5734     ## Step 8
5735     redo S7;
5736     } # S7
5737    
5738     ## Step 8
5739 wakaba 1.121 if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
5740 wakaba 1.102 my $foster_parent_element;
5741     my $next_sibling;
5742 wakaba 1.121 OE: for (reverse 0..$#{$self->{open_elements}}) {
5743     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5744 wakaba 1.102 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5745     if (defined $parent and $parent->node_type == 1) {
5746    
5747     $foster_parent_element = $parent;
5748     $next_sibling = $self->{open_elements}->[$_]->[0];
5749     } else {
5750    
5751     $foster_parent_element
5752     = $self->{open_elements}->[$_ - 1]->[0];
5753     }
5754     last OE;
5755     }
5756     } # OE
5757     $foster_parent_element = $self->{open_elements}->[0]->[0]
5758     unless defined $foster_parent_element;
5759     $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
5760     $open_tables->[-1]->[1] = 1; # tainted
5761     } else {
5762    
5763     $common_ancestor_node->[0]->append_child ($last_node->[0]);
5764     }
5765 wakaba 1.1
5766     ## Step 9
5767     my $clone = [$formatting_element->[0]->clone_node (0),
5768     $formatting_element->[1]];
5769    
5770     ## Step 10
5771     my @cn = @{$furthest_block->[0]->child_nodes};
5772     $clone->[0]->append_child ($_) for @cn;
5773    
5774     ## Step 11
5775     $furthest_block->[0]->append_child ($clone->[0]);
5776    
5777     ## Step 12
5778     my $i;
5779     AFE: for (reverse 0..$#$active_formatting_elements) {
5780     if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
5781 wakaba 1.79
5782 wakaba 1.1 splice @$active_formatting_elements, $_, 1;
5783     $i-- and last AFE if defined $i;
5784     } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
5785 wakaba 1.79
5786 wakaba 1.1 $i = $_;
5787     }
5788     } # AFE
5789     splice @$active_formatting_elements, $i + 1, 0, $clone;
5790    
5791     ## Step 13
5792     undef $i;
5793 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
5794     if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
5795 wakaba 1.79
5796 wakaba 1.3 splice @{$self->{open_elements}}, $_, 1;
5797 wakaba 1.1 $i-- and last OE if defined $i;
5798 wakaba 1.3 } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
5799 wakaba 1.79
5800 wakaba 1.1 $i = $_;
5801     }
5802     } # OE
5803 wakaba 1.198 splice @{$self->{open_elements}}, $i + 1, 0, $clone;
5804 wakaba 1.1
5805     ## Step 14
5806     redo FET;
5807     } # FET
5808     }; # $formatting_end_tag
5809    
5810 wakaba 1.96 $insert = my $insert_to_current = sub {
5811 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
5812 wakaba 1.1 }; # $insert_to_current
5813    
5814     my $insert_to_foster = sub {
5815 wakaba 1.95 my $child = shift;
5816 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5817 wakaba 1.95 # MUST
5818     my $foster_parent_element;
5819     my $next_sibling;
5820 wakaba 1.121 OE: for (reverse 0..$#{$self->{open_elements}}) {
5821     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5822 wakaba 1.3 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5823 wakaba 1.1 if (defined $parent and $parent->node_type == 1) {
5824 wakaba 1.79
5825 wakaba 1.1 $foster_parent_element = $parent;
5826 wakaba 1.3 $next_sibling = $self->{open_elements}->[$_]->[0];
5827 wakaba 1.1 } else {
5828 wakaba 1.79
5829 wakaba 1.1 $foster_parent_element
5830 wakaba 1.3 = $self->{open_elements}->[$_ - 1]->[0];
5831 wakaba 1.1 }
5832     last OE;
5833     }
5834     } # OE
5835 wakaba 1.3 $foster_parent_element = $self->{open_elements}->[0]->[0]
5836 wakaba 1.1 unless defined $foster_parent_element;
5837     $foster_parent_element->insert_before
5838     ($child, $next_sibling);
5839 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
5840     } else {
5841    
5842     $self->{open_elements}->[-1]->[0]->append_child ($child);
5843     }
5844 wakaba 1.1 }; # $insert_to_foster
5845    
5846 wakaba 1.199 ## NOTE: Insert a character (MUST): When a character is inserted, if
5847     ## the last node that was inserted by the parser is a Text node and
5848     ## the character has to be inserted after that node, then the
5849     ## character is appended to the Text node. However, if any other
5850     ## node is inserted by the parser, then a new Text node is created
5851     ## and the character is appended as that Text node. If I'm not
5852     ## wrong, for a parser with scripting disabled, there are only two
5853     ## cases where this occurs. One is the case where an element node
5854     ## is inserted to the |head| element. This is covered by using the
5855 wakaba 1.197 ## |$self->{head_element_inserted}| flag. Another is the case where
5856     ## an element or comment is inserted into the |table| subtree while
5857     ## foster parenting happens. This is covered by using the [2] flag
5858     ## of the |$open_tables| structure. All other cases are handled
5859     ## simply by calling |manakai_append_text| method.
5860    
5861 wakaba 1.199 ## TODO: |<body><script>document.write("a<br>");
5862     ## document.body.removeChild (document.body.lastChild);
5863     ## document.write ("b")</script>|
5864    
5865 wakaba 1.123 B: while (1) {
5866 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
5867 wakaba 1.79
5868 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in html:#DOCTYPE', token => $token);
5869 wakaba 1.54 ## Ignore the token
5870     ## Stay in the phase
5871     $token = $self->_get_next_token;
5872 wakaba 1.123 next B;
5873 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN and
5874 wakaba 1.54 $token->{tag_name} eq 'html') {
5875 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5876 wakaba 1.79
5877 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html', text => 'html', token => $token);
5878 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
5879     } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5880 wakaba 1.79
5881 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html', text => 'html', token => $token);
5882 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
5883 wakaba 1.79 } else {
5884    
5885 wakaba 1.54 }
5886    
5887 wakaba 1.84
5888 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not first start tag', token => $token);
5889 wakaba 1.54 my $top_el = $self->{open_elements}->[0]->[0];
5890     for my $attr_name (keys %{$token->{attributes}}) {
5891     unless ($top_el->has_attribute_ns (undef, $attr_name)) {
5892 wakaba 1.79
5893 wakaba 1.54 $top_el->set_attribute_ns
5894     (undef, [undef, $attr_name],
5895     $token->{attributes}->{$attr_name}->{value});
5896     }
5897     }
5898 wakaba 1.122
5899 wakaba 1.54 $token = $self->_get_next_token;
5900 wakaba 1.123 next B;
5901 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
5902 wakaba 1.54 my $comment = $self->{document}->create_comment ($token->{data});
5903 wakaba 1.58 if ($self->{insertion_mode} & AFTER_HTML_IMS) {
5904 wakaba 1.79
5905 wakaba 1.54 $self->{document}->append_child ($comment);
5906 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
5907 wakaba 1.79
5908 wakaba 1.54 $self->{open_elements}->[0]->[0]->append_child ($comment);
5909     } else {
5910 wakaba 1.79
5911 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($comment);
5912 wakaba 1.197 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5913 wakaba 1.54 }
5914     $token = $self->_get_next_token;
5915 wakaba 1.123 next B;
5916 wakaba 1.200 } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
5917     if ($token->{type} == CHARACTER_TOKEN) {
5918     $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
5919     delete $self->{ignore_newline};
5920    
5921     if (length $token->{data}) {
5922    
5923     $self->{open_elements}->[-1]->[0]->manakai_append_text
5924     ($token->{data});
5925     } else {
5926    
5927     }
5928     $token = $self->_get_next_token;
5929     next B;
5930     } elsif ($token->{type} == END_TAG_TOKEN) {
5931     delete $self->{ignore_newline};
5932    
5933     if ($token->{tag_name} eq 'script') {
5934    
5935    
5936     ## Para 1-2
5937     my $script = pop @{$self->{open_elements}};
5938    
5939     ## Para 3
5940     $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
5941    
5942     ## Para 4
5943     ## TODO: $old_insertion_point = $current_insertion_point;
5944     ## TODO: $current_insertion_point = just before $self->{nc};
5945    
5946     ## Para 5
5947     ## TODO: Run the $script->[0].
5948    
5949     ## Para 6
5950     ## TODO: $current_insertion_point = $old_insertion_point;
5951    
5952     ## Para 7
5953     ## TODO: if ($pending_external_script) {
5954     ## TODO: ...
5955     ## TODO: }
5956    
5957     $token = $self->_get_next_token;
5958     next B;
5959     } else {
5960    
5961    
5962     pop @{$self->{open_elements}};
5963    
5964     $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
5965     $token = $self->_get_next_token;
5966     next B;
5967     }
5968     } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5969     delete $self->{ignore_newline};
5970    
5971    
5972     $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
5973     text => $self->{open_elements}->[-1]->[0]
5974     ->manakai_local_name,
5975     token => $token);
5976    
5977     #if ($self->{open_elements}->[-1]->[1] & SCRIPT_EL) {
5978     # ## TODO: Mark as "already executed"
5979     #}
5980    
5981     pop @{$self->{open_elements}};
5982    
5983     $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
5984     ## Reprocess.
5985     next B;
5986     } else {
5987     die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";
5988     }
5989 wakaba 1.123 } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
5990     if ($token->{type} == CHARACTER_TOKEN) {
5991    
5992     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5993     $token = $self->_get_next_token;
5994     next B;
5995     } elsif ($token->{type} == START_TAG_TOKEN) {
5996 wakaba 1.126 if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
5997     $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
5998 wakaba 1.123 not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
5999     ($token->{tag_name} eq 'svg' and
6000     $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
6001     ## NOTE: "using the rules for secondary insertion mode"then"continue"
6002    
6003     #
6004     } elsif ({
6005 wakaba 1.127 b => 1, big => 1, blockquote => 1, body => 1, br => 1,
6006 wakaba 1.143 center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
6007     em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
6008     h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
6009     img => 1, li => 1, listing => 1, menu => 1, meta => 1,
6010     nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
6011     small => 1, span => 1, strong => 1, strike => 1, sub => 1,
6012     sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
6013 wakaba 1.123 }->{$token->{tag_name}}) {
6014    
6015 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
6016     text => $self->{open_elements}->[-1]->[0]
6017 wakaba 1.123 ->manakai_local_name,
6018     token => $token);
6019    
6020     pop @{$self->{open_elements}}
6021     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
6022    
6023 wakaba 1.127 $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
6024 wakaba 1.123 ## Reprocess.
6025     next B;
6026     } else {
6027 wakaba 1.128 my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
6028     my $tag_name = $token->{tag_name};
6029     if ($nsuri eq $SVG_NS) {
6030     $tag_name = {
6031     altglyph => 'altGlyph',
6032     altglyphdef => 'altGlyphDef',
6033     altglyphitem => 'altGlyphItem',
6034     animatecolor => 'animateColor',
6035     animatemotion => 'animateMotion',
6036     animatetransform => 'animateTransform',
6037     clippath => 'clipPath',
6038     feblend => 'feBlend',
6039     fecolormatrix => 'feColorMatrix',
6040     fecomponenttransfer => 'feComponentTransfer',
6041     fecomposite => 'feComposite',
6042     feconvolvematrix => 'feConvolveMatrix',
6043     fediffuselighting => 'feDiffuseLighting',
6044     fedisplacementmap => 'feDisplacementMap',
6045     fedistantlight => 'feDistantLight',
6046     feflood => 'feFlood',
6047     fefunca => 'feFuncA',
6048     fefuncb => 'feFuncB',
6049     fefuncg => 'feFuncG',
6050     fefuncr => 'feFuncR',
6051     fegaussianblur => 'feGaussianBlur',
6052     feimage => 'feImage',
6053     femerge => 'feMerge',
6054     femergenode => 'feMergeNode',
6055     femorphology => 'feMorphology',
6056     feoffset => 'feOffset',
6057     fepointlight => 'fePointLight',
6058     fespecularlighting => 'feSpecularLighting',
6059     fespotlight => 'feSpotLight',
6060     fetile => 'feTile',
6061     feturbulence => 'feTurbulence',
6062     foreignobject => 'foreignObject',
6063     glyphref => 'glyphRef',
6064     lineargradient => 'linearGradient',
6065     radialgradient => 'radialGradient',
6066     #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
6067     textpath => 'textPath',
6068     }->{$tag_name} || $tag_name;
6069     }
6070    
6071     ## "adjust SVG attributes" (SVG only) - done in insert-element-f
6072    
6073     ## "adjust foreign attributes" - done in insert-element-f
6074 wakaba 1.123
6075    
6076     {
6077     my $el;
6078    
6079     $el = $self->{document}->create_element_ns
6080 wakaba 1.128 ($nsuri, [undef, $tag_name]);
6081 wakaba 1.123
6082     for my $attr_name (keys %{ $token->{attributes}}) {
6083     my $attr_t = $token->{attributes}->{$attr_name};
6084 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (
6085     @{
6086     $foreign_attr_xname->{$attr_name} ||
6087     [undef, [undef,
6088 wakaba 1.152 ($nsuri) eq $SVG_NS ?
6089 wakaba 1.128 ($svg_attr_name->{$attr_name} || $attr_name) :
6090 wakaba 1.152 ($nsuri) eq $MML_NS ?
6091     ($attr_name eq 'definitionurl' ?
6092     'definitionURL' : $attr_name) :
6093 wakaba 1.128 $attr_name]]
6094     }
6095     );
6096 wakaba 1.123 $attr->value ($attr_t->{value});
6097     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6098     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6099     $el->set_attribute_node_ns ($attr);
6100     }
6101    
6102     $el->set_user_data (manakai_source_line => $token->{line})
6103     if defined $token->{line};
6104     $el->set_user_data (manakai_source_column => $token->{column})
6105     if defined $token->{column};
6106    
6107     $insert->($el);
6108 wakaba 1.128 push @{$self->{open_elements}}, [$el, ($el_category_f->{$nsuri}->{ $tag_name} || 0) | FOREIGN_EL];
6109    
6110     if ( $token->{attributes}->{xmlns} and $token->{attributes}->{xmlns}->{value} ne ($nsuri)) {
6111 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad namespace', token => $token);
6112 wakaba 1.128 ## TODO: Error type documentation
6113     }
6114 wakaba 1.123 }
6115    
6116    
6117     if ($self->{self_closing}) {
6118     pop @{$self->{open_elements}};
6119     delete $self->{self_closing};
6120     } else {
6121    
6122     }
6123    
6124     $token = $self->_get_next_token;
6125     next B;
6126     }
6127     } elsif ($token->{type} == END_TAG_TOKEN) {
6128     ## NOTE: "using the rules for secondary insertion mode" then "continue"
6129    
6130     #
6131     } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6132    
6133 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
6134     text => $self->{open_elements}->[-1]->[0]
6135 wakaba 1.143 ->manakai_local_name,
6136     token => $token);
6137    
6138     pop @{$self->{open_elements}}
6139     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
6140    
6141 wakaba 1.196 ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
6142    
6143 wakaba 1.143 $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
6144     ## Reprocess.
6145     next B;
6146 wakaba 1.123 } else {
6147     die "$0: $token->{type}: Unknown token type";
6148     }
6149     }
6150    
6151     if ($self->{insertion_mode} & HEAD_IMS) {
6152 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
6153 wakaba 1.184 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6154 wakaba 1.99 unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6155 wakaba 1.197 if ($self->{head_element_inserted}) {
6156    
6157     $self->{open_elements}->[-1]->[0]->append_child
6158     ($self->{document}->create_text_node ($1));
6159     delete $self->{head_element_inserted};
6160     ## NOTE: |</head> <link> |
6161     #
6162     } else {
6163    
6164     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6165     ## NOTE: |</head> &#x20;|
6166     #
6167     }
6168 wakaba 1.99 } else {
6169    
6170     ## Ignore the token.
6171 wakaba 1.174 #
6172 wakaba 1.99 }
6173 wakaba 1.54 unless (length $token->{data}) {
6174 wakaba 1.79
6175 wakaba 1.54 $token = $self->_get_next_token;
6176 wakaba 1.123 next B;
6177 wakaba 1.54 }
6178 wakaba 1.173 ## TODO: set $token->{column} appropriately
6179 wakaba 1.54 }
6180    
6181 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6182 wakaba 1.79
6183 wakaba 1.54 ## As if <head>
6184    
6185     $self->{head_element} = $self->{document}->create_element_ns
6186 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6187 wakaba 1.54
6188 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6189     if defined $token->{line};
6190     $self->{head_element}->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 ($self->{head_element});
6194 wakaba 1.121 push @{$self->{open_elements}},
6195     [$self->{head_element}, $el_category->{head}];
6196 wakaba 1.54
6197     ## Reprocess in the "in head" insertion mode...
6198     pop @{$self->{open_elements}};
6199    
6200     ## Reprocess in the "after head" insertion mode...
6201 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6202 wakaba 1.79
6203 wakaba 1.54 ## As if </noscript>
6204     pop @{$self->{open_elements}};
6205 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:#text', token => $token);
6206 wakaba 1.54
6207     ## Reprocess in the "in head" insertion mode...
6208     ## As if </head>
6209     pop @{$self->{open_elements}};
6210    
6211     ## Reprocess in the "after head" insertion mode...
6212 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6213 wakaba 1.79
6214 wakaba 1.54 pop @{$self->{open_elements}};
6215    
6216     ## Reprocess in the "after head" insertion mode...
6217 wakaba 1.79 } else {
6218    
6219 wakaba 1.54 }
6220    
6221 wakaba 1.121 ## "after head" insertion mode
6222     ## As if <body>
6223    
6224 wakaba 1.54 {
6225     my $el;
6226    
6227     $el = $self->{document}->create_element_ns
6228 wakaba 1.128 ($HTML_NS, [undef, 'body']);
6229 wakaba 1.54
6230 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6231     if defined $token->{line};
6232     $el->set_user_data (manakai_source_column => $token->{column})
6233     if defined $token->{column};
6234    
6235 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6236 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
6237 wakaba 1.54 }
6238    
6239 wakaba 1.121 $self->{insertion_mode} = IN_BODY_IM;
6240     ## reprocess
6241 wakaba 1.123 next B;
6242 wakaba 1.121 } elsif ($token->{type} == START_TAG_TOKEN) {
6243     if ($token->{tag_name} eq 'head') {
6244     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6245    
6246    
6247 wakaba 1.54 $self->{head_element} = $self->{document}->create_element_ns
6248 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6249 wakaba 1.54
6250     for my $attr_name (keys %{ $token->{attributes}}) {
6251 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6252 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6253 wakaba 1.117 $attr->value ($attr_t->{value});
6254     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6255     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6256     $self->{head_element}->set_attribute_node_ns ($attr);
6257 wakaba 1.54 }
6258    
6259 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6260     if defined $token->{line};
6261     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6262     if defined $token->{column};
6263    
6264 wakaba 1.121 $self->{open_elements}->[-1]->[0]->append_child
6265     ($self->{head_element});
6266     push @{$self->{open_elements}},
6267     [$self->{head_element}, $el_category->{head}];
6268     $self->{insertion_mode} = IN_HEAD_IM;
6269 wakaba 1.122
6270     $token = $self->_get_next_token;
6271 wakaba 1.123 next B;
6272 wakaba 1.122 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6273    
6274 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head', text => 'head',
6275     token => $token);
6276 wakaba 1.136 ## Ignore the token
6277    
6278     $token = $self->_get_next_token;
6279     next B;
6280 wakaba 1.122 } else {
6281    
6282 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in head:head',
6283     token => $token); # or in head noscript
6284 wakaba 1.122 ## Ignore the token
6285    
6286 wakaba 1.121 $token = $self->_get_next_token;
6287 wakaba 1.123 next B;
6288 wakaba 1.122 }
6289     } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6290 wakaba 1.123
6291     ## As if <head>
6292    
6293 wakaba 1.54 $self->{head_element} = $self->{document}->create_element_ns
6294 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6295 wakaba 1.54
6296 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6297     if defined $token->{line};
6298     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6299     if defined $token->{column};
6300    
6301 wakaba 1.123 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6302     push @{$self->{open_elements}},
6303     [$self->{head_element}, $el_category->{head}];
6304 wakaba 1.54
6305 wakaba 1.123 $self->{insertion_mode} = IN_HEAD_IM;
6306     ## Reprocess in the "in head" insertion mode...
6307     } else {
6308    
6309     }
6310 wakaba 1.54
6311 wakaba 1.197 if ($token->{tag_name} eq 'base') {
6312     if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6313    
6314     ## As if </noscript>
6315     pop @{$self->{open_elements}};
6316     $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'base',
6317     token => $token);
6318    
6319     $self->{insertion_mode} = IN_HEAD_IM;
6320     ## Reprocess in the "in head" insertion mode...
6321     } else {
6322    
6323     }
6324 wakaba 1.54
6325 wakaba 1.197 ## NOTE: There is a "as if in head" code clone.
6326     if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6327    
6328     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6329     text => $token->{tag_name}, token => $token);
6330     push @{$self->{open_elements}},
6331     [$self->{head_element}, $el_category->{head}];
6332     $self->{head_element_inserted} = 1;
6333     } else {
6334    
6335     }
6336    
6337 wakaba 1.54 {
6338     my $el;
6339    
6340     $el = $self->{document}->create_element_ns
6341 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6342 wakaba 1.54
6343     for my $attr_name (keys %{ $token->{attributes}}) {
6344 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6345 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6346 wakaba 1.117 $attr->value ($attr_t->{value});
6347     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6348     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6349     $el->set_attribute_node_ns ($attr);
6350 wakaba 1.54 }
6351    
6352 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6353     if defined $token->{line};
6354     $el->set_user_data (manakai_source_column => $token->{column})
6355     if defined $token->{column};
6356    
6357 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6358 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6359 wakaba 1.54 }
6360    
6361 wakaba 1.197 pop @{$self->{open_elements}};
6362     pop @{$self->{open_elements}} # <head>
6363     if $self->{insertion_mode} == AFTER_HEAD_IM;
6364    
6365     $token = $self->_get_next_token;
6366     next B;
6367 wakaba 1.189 } elsif ($token->{tag_name} eq 'link') {
6368     ## NOTE: There is a "as if in head" code clone.
6369     if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6370    
6371     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6372     text => $token->{tag_name}, token => $token);
6373     push @{$self->{open_elements}},
6374     [$self->{head_element}, $el_category->{head}];
6375 wakaba 1.197 $self->{head_element_inserted} = 1;
6376 wakaba 1.189 } else {
6377    
6378     }
6379    
6380     {
6381     my $el;
6382    
6383     $el = $self->{document}->create_element_ns
6384     ($HTML_NS, [undef, $token->{tag_name}]);
6385    
6386     for my $attr_name (keys %{ $token->{attributes}}) {
6387     my $attr_t = $token->{attributes}->{$attr_name};
6388     my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6389     $attr->value ($attr_t->{value});
6390     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6391     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6392     $el->set_attribute_node_ns ($attr);
6393     }
6394    
6395     $el->set_user_data (manakai_source_line => $token->{line})
6396     if defined $token->{line};
6397     $el->set_user_data (manakai_source_column => $token->{column})
6398     if defined $token->{column};
6399    
6400     $self->{open_elements}->[-1]->[0]->append_child ($el);
6401     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6402     }
6403    
6404     pop @{$self->{open_elements}};
6405     pop @{$self->{open_elements}} # <head>
6406     if $self->{insertion_mode} == AFTER_HEAD_IM;
6407     delete $self->{self_closing};
6408     $token = $self->_get_next_token;
6409     next B;
6410     } elsif ($token->{tag_name} eq 'command' or
6411     $token->{tag_name} eq 'eventsource') {
6412     if ($self->{insertion_mode} == IN_HEAD_IM) {
6413     ## NOTE: If the insertion mode at the time of the emission
6414     ## of the token was "before head", $self->{insertion_mode}
6415     ## is already changed to |IN_HEAD_IM|.
6416    
6417     ## NOTE: There is a "as if in head" code clone.
6418    
6419 wakaba 1.25 {
6420     my $el;
6421    
6422 wakaba 1.1 $el = $self->{document}->create_element_ns
6423 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6424 wakaba 1.1
6425 wakaba 1.25 for my $attr_name (keys %{ $token->{attributes}}) {
6426 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6427 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6428 wakaba 1.117 $attr->value ($attr_t->{value});
6429     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6430     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6431     $el->set_attribute_node_ns ($attr);
6432 wakaba 1.1 }
6433    
6434 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6435     if defined $token->{line};
6436     $el->set_user_data (manakai_source_column => $token->{column})
6437     if defined $token->{column};
6438    
6439 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6440 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6441 wakaba 1.25 }
6442    
6443 wakaba 1.189 pop @{$self->{open_elements}};
6444     pop @{$self->{open_elements}} # <head>
6445     if $self->{insertion_mode} == AFTER_HEAD_IM;
6446     delete $self->{self_closing};
6447     $token = $self->_get_next_token;
6448     next B;
6449     } else {
6450     ## NOTE: "in head noscript" or "after head" insertion mode
6451     ## - in these cases, these tags are treated as same as
6452     ## normal in-body tags.
6453    
6454     #
6455     }
6456 wakaba 1.197 } elsif ($token->{tag_name} eq 'meta') {
6457     ## NOTE: There is a "as if in head" code clone.
6458     if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6459    
6460     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6461     text => $token->{tag_name}, token => $token);
6462     push @{$self->{open_elements}},
6463     [$self->{head_element}, $el_category->{head}];
6464     $self->{head_element_inserted} = 1;
6465     } else {
6466    
6467     }
6468    
6469 wakaba 1.34 {
6470     my $el;
6471    
6472     $el = $self->{document}->create_element_ns
6473 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6474 wakaba 1.34
6475     for my $attr_name (keys %{ $token->{attributes}}) {
6476 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6477 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6478 wakaba 1.117 $attr->value ($attr_t->{value});
6479     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6480     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6481     $el->set_attribute_node_ns ($attr);
6482 wakaba 1.34 }
6483    
6484 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6485     if defined $token->{line};
6486     $el->set_user_data (manakai_source_column => $token->{column})
6487     if defined $token->{column};
6488    
6489 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6490 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6491 wakaba 1.34 }
6492    
6493 wakaba 1.197 my $meta_el = pop @{$self->{open_elements}};
6494 wakaba 1.34
6495 wakaba 1.54 unless ($self->{confident}) {
6496 wakaba 1.131 if ($token->{attributes}->{charset}) {
6497 wakaba 1.79
6498 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
6499     ## in the {change_encoding} callback.
6500 wakaba 1.65 $self->{change_encoding}
6501 wakaba 1.112 ->($self, $token->{attributes}->{charset}->{value},
6502     $token);
6503 wakaba 1.68
6504     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6505     ->set_user_data (manakai_has_reference =>
6506     $token->{attributes}->{charset}
6507     ->{has_reference});
6508 wakaba 1.65 } elsif ($token->{attributes}->{content}) {
6509     if ($token->{attributes}->{content}->{value}
6510 wakaba 1.141 =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6511 wakaba 1.183 [\x09\x0A\x0C\x0D\x20]*=
6512     [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6513     ([^"'\x09\x0A\x0C\x0D\x20]
6514     [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
6515 wakaba 1.79
6516 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
6517     ## in the {change_encoding} callback.
6518 wakaba 1.65 $self->{change_encoding}
6519 wakaba 1.112 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
6520     $token);
6521 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6522     ->set_user_data (manakai_has_reference =>
6523     $token->{attributes}->{content}
6524     ->{has_reference});
6525 wakaba 1.79 } else {
6526    
6527 wakaba 1.65 }
6528 wakaba 1.54 }
6529 wakaba 1.68 } else {
6530     if ($token->{attributes}->{charset}) {
6531 wakaba 1.79
6532 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6533     ->set_user_data (manakai_has_reference =>
6534     $token->{attributes}->{charset}
6535     ->{has_reference});
6536     }
6537 wakaba 1.69 if ($token->{attributes}->{content}) {
6538 wakaba 1.79
6539 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6540     ->set_user_data (manakai_has_reference =>
6541     $token->{attributes}->{content}
6542     ->{has_reference});
6543     }
6544 wakaba 1.54 }
6545 wakaba 1.34
6546 wakaba 1.100 pop @{$self->{open_elements}} # <head>
6547 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
6548 wakaba 1.122 delete $self->{self_closing};
6549 wakaba 1.54 $token = $self->_get_next_token;
6550 wakaba 1.123 next B;
6551 wakaba 1.197 } elsif ($token->{tag_name} eq 'title') {
6552     if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6553    
6554     ## As if </noscript>
6555     pop @{$self->{open_elements}};
6556     $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'title',
6557     token => $token);
6558    
6559     $self->{insertion_mode} = IN_HEAD_IM;
6560     ## Reprocess in the "in head" insertion mode...
6561     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6562    
6563     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6564     text => $token->{tag_name}, token => $token);
6565     push @{$self->{open_elements}},
6566     [$self->{head_element}, $el_category->{head}];
6567     $self->{head_element_inserted} = 1;
6568     } else {
6569    
6570     }
6571 wakaba 1.54
6572 wakaba 1.197 ## NOTE: There is a "as if in head" code clone.
6573     $parse_rcdata->(RCDATA_CONTENT_MODEL);
6574 wakaba 1.200 ## ISSUE: A spec bug.
6575     splice @{$self->{open_elements}}, -2, 1, () # <head>
6576     if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
6577 wakaba 1.197 next B;
6578     } elsif ($token->{tag_name} eq 'style' or
6579     $token->{tag_name} eq 'noframes') {
6580     ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
6581     ## insertion mode IN_HEAD_IM)
6582     ## NOTE: There is a "as if in head" code clone.
6583     if ($self->{insertion_mode} == AFTER_HEAD_IM) {
6584    
6585     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6586     text => $token->{tag_name}, token => $token);
6587     push @{$self->{open_elements}},
6588     [$self->{head_element}, $el_category->{head}];
6589     $self->{head_element_inserted} = 1;
6590     } else {
6591    
6592     }
6593     $parse_rcdata->(CDATA_CONTENT_MODEL);
6594 wakaba 1.200 ## ISSUE: A spec bug.
6595     splice @{$self->{open_elements}}, -2, 1, () # <head>
6596     if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
6597 wakaba 1.197 next B;
6598 wakaba 1.200 } elsif ($token->{tag_name} eq 'noscript') {
6599 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_IM) {
6600 wakaba 1.79
6601 wakaba 1.54 ## NOTE: and scripting is disalbed
6602    
6603 wakaba 1.1 {
6604     my $el;
6605    
6606     $el = $self->{document}->create_element_ns
6607 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6608 wakaba 1.1
6609     for my $attr_name (keys %{ $token->{attributes}}) {
6610 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6611 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6612 wakaba 1.117 $attr->value ($attr_t->{value});
6613     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6614     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6615     $el->set_attribute_node_ns ($attr);
6616 wakaba 1.1 }
6617    
6618 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6619     if defined $token->{line};
6620     $el->set_user_data (manakai_source_column => $token->{column})
6621     if defined $token->{column};
6622    
6623 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6624 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6625 wakaba 1.1 }
6626    
6627 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
6628 wakaba 1.122
6629 wakaba 1.54 $token = $self->_get_next_token;
6630 wakaba 1.123 next B;
6631 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6632 wakaba 1.79
6633 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'noscript',
6634     token => $token);
6635 wakaba 1.54 ## Ignore the token
6636 wakaba 1.122
6637 wakaba 1.54 $token = $self->_get_next_token;
6638 wakaba 1.123 next B;
6639 wakaba 1.54 } else {
6640 wakaba 1.79
6641 wakaba 1.54 #
6642     }
6643 wakaba 1.197 } elsif ($token->{tag_name} eq 'script') {
6644     if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6645    
6646     ## As if </noscript>
6647     pop @{$self->{open_elements}};
6648     $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript', text => 'script',
6649     token => $token);
6650    
6651     $self->{insertion_mode} = IN_HEAD_IM;
6652     ## Reprocess in the "in head" insertion mode...
6653     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6654    
6655     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after head',
6656     text => $token->{tag_name}, token => $token);
6657     push @{$self->{open_elements}},
6658     [$self->{head_element}, $el_category->{head}];
6659     $self->{head_element_inserted} = 1;
6660     } else {
6661    
6662     }
6663 wakaba 1.54
6664 wakaba 1.197 ## NOTE: There is a "as if in head" code clone.
6665     $script_start_tag->();
6666 wakaba 1.200 ## ISSUE: A spec bug.
6667     splice @{$self->{open_elements}}, -2, 1 # <head>
6668     if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
6669 wakaba 1.197 next B;
6670     } elsif ($token->{tag_name} eq 'body' or
6671     $token->{tag_name} eq 'frameset') {
6672 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6673 wakaba 1.79
6674 wakaba 1.54 ## As if </noscript>
6675     pop @{$self->{open_elements}};
6676 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript',
6677     text => $token->{tag_name}, token => $token);
6678 wakaba 1.54
6679     ## Reprocess in the "in head" insertion mode...
6680     ## As if </head>
6681     pop @{$self->{open_elements}};
6682    
6683     ## Reprocess in the "after head" insertion mode...
6684 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6685 wakaba 1.79
6686 wakaba 1.54 pop @{$self->{open_elements}};
6687    
6688     ## Reprocess in the "after head" insertion mode...
6689 wakaba 1.79 } else {
6690    
6691 wakaba 1.54 }
6692    
6693     ## "after head" insertion mode
6694    
6695 wakaba 1.1 {
6696     my $el;
6697    
6698     $el = $self->{document}->create_element_ns
6699 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6700 wakaba 1.1
6701     for my $attr_name (keys %{ $token->{attributes}}) {
6702 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6703 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6704 wakaba 1.117 $attr->value ($attr_t->{value});
6705     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6706     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6707     $el->set_attribute_node_ns ($attr);
6708 wakaba 1.1 }
6709    
6710 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6711     if defined $token->{line};
6712     $el->set_user_data (manakai_source_column => $token->{column})
6713     if defined $token->{column};
6714    
6715 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6716 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6717 wakaba 1.1 }
6718    
6719 wakaba 1.56 if ($token->{tag_name} eq 'body') {
6720 wakaba 1.79
6721 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
6722     } elsif ($token->{tag_name} eq 'frameset') {
6723 wakaba 1.79
6724 wakaba 1.56 $self->{insertion_mode} = IN_FRAMESET_IM;
6725     } else {
6726     die "$0: tag name: $self->{tag_name}";
6727     }
6728 wakaba 1.122
6729 wakaba 1.54 $token = $self->_get_next_token;
6730 wakaba 1.123 next B;
6731 wakaba 1.54 } else {
6732 wakaba 1.79
6733 wakaba 1.54 #
6734 wakaba 1.8 }
6735 wakaba 1.54
6736 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6737 wakaba 1.79
6738 wakaba 1.54 ## As if </noscript>
6739     pop @{$self->{open_elements}};
6740 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
6741     text => $token->{tag_name}, token => $token);
6742 wakaba 1.54
6743     ## Reprocess in the "in head" insertion mode...
6744     ## As if </head>
6745     pop @{$self->{open_elements}};
6746    
6747     ## Reprocess in the "after head" insertion mode...
6748 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6749 wakaba 1.79
6750 wakaba 1.54 ## As if </head>
6751     pop @{$self->{open_elements}};
6752    
6753     ## Reprocess in the "after head" insertion mode...
6754 wakaba 1.79 } else {
6755    
6756 wakaba 1.54 }
6757    
6758     ## "after head" insertion mode
6759     ## As if <body>
6760    
6761 wakaba 1.1 {
6762     my $el;
6763    
6764     $el = $self->{document}->create_element_ns
6765 wakaba 1.128 ($HTML_NS, [undef, 'body']);
6766 wakaba 1.1
6767 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6768     if defined $token->{line};
6769     $el->set_user_data (manakai_source_column => $token->{column})
6770     if defined $token->{column};
6771    
6772 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6773 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
6774 wakaba 1.1 }
6775    
6776 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
6777 wakaba 1.54 ## reprocess
6778 wakaba 1.122
6779 wakaba 1.123 next B;
6780 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
6781 wakaba 1.54 if ($token->{tag_name} eq 'head') {
6782 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6783 wakaba 1.79
6784 wakaba 1.54 ## As if <head>
6785    
6786     $self->{head_element} = $self->{document}->create_element_ns
6787 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6788 wakaba 1.54
6789 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6790     if defined $token->{line};
6791     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6792     if defined $token->{column};
6793    
6794 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6795 wakaba 1.121 push @{$self->{open_elements}},
6796     [$self->{head_element}, $el_category->{head}];
6797 wakaba 1.54
6798     ## Reprocess in the "in head" insertion mode...
6799     pop @{$self->{open_elements}};
6800 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
6801 wakaba 1.54 $token = $self->_get_next_token;
6802 wakaba 1.123 next B;
6803 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6804 wakaba 1.79
6805 wakaba 1.54 ## As if </noscript>
6806     pop @{$self->{open_elements}};
6807 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
6808     text => 'head', token => $token);
6809 wakaba 1.54
6810     ## Reprocess in the "in head" insertion mode...
6811     pop @{$self->{open_elements}};
6812 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
6813 wakaba 1.54 $token = $self->_get_next_token;
6814 wakaba 1.123 next B;
6815 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6816 wakaba 1.79
6817 wakaba 1.54 pop @{$self->{open_elements}};
6818 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
6819 wakaba 1.54 $token = $self->_get_next_token;
6820 wakaba 1.123 next B;
6821 wakaba 1.136 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6822    
6823 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => 'head',
6824     token => $token);
6825 wakaba 1.136 ## Ignore the token
6826     $token = $self->_get_next_token;
6827     next B;
6828 wakaba 1.54 } else {
6829 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
6830 wakaba 1.54 }
6831     } elsif ($token->{tag_name} eq 'noscript') {
6832 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6833 wakaba 1.79
6834 wakaba 1.54 pop @{$self->{open_elements}};
6835 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
6836 wakaba 1.54 $token = $self->_get_next_token;
6837 wakaba 1.123 next B;
6838 wakaba 1.136 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
6839     $self->{insertion_mode} == AFTER_HEAD_IM) {
6840 wakaba 1.79
6841 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6842     text => 'noscript', token => $token);
6843 wakaba 1.54 ## Ignore the token ## ISSUE: An issue in the spec.
6844     $token = $self->_get_next_token;
6845 wakaba 1.123 next B;
6846 wakaba 1.54 } else {
6847 wakaba 1.79
6848 wakaba 1.54 #
6849     }
6850     } elsif ({
6851     body => 1, html => 1,
6852     }->{$token->{tag_name}}) {
6853 wakaba 1.198 ## TODO: This branch is entirely redundant.
6854     if ($self->{insertion_mode} == BEFORE_HEAD_IM or
6855 wakaba 1.136 $self->{insertion_mode} == IN_HEAD_IM or
6856     $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6857 wakaba 1.79
6858 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6859     text => $token->{tag_name}, token => $token);
6860 wakaba 1.136 ## Ignore the token
6861     $token = $self->_get_next_token;
6862     next B;
6863     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6864 wakaba 1.79
6865 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6866     text => $token->{tag_name}, token => $token);
6867 wakaba 1.54 ## Ignore the token
6868     $token = $self->_get_next_token;
6869 wakaba 1.123 next B;
6870 wakaba 1.79 } else {
6871 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
6872 wakaba 1.54 }
6873 wakaba 1.136 } elsif ($token->{tag_name} eq 'p') {
6874    
6875 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6876     text => $token->{tag_name}, token => $token);
6877 wakaba 1.136 ## Ignore the token
6878     $token = $self->_get_next_token;
6879     next B;
6880     } elsif ($token->{tag_name} eq 'br') {
6881 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6882 wakaba 1.79
6883 wakaba 1.136 ## (before head) as if <head>, (in head) as if </head>
6884 wakaba 1.54
6885     $self->{head_element} = $self->{document}->create_element_ns
6886 wakaba 1.128 ($HTML_NS, [undef, 'head']);
6887 wakaba 1.54
6888 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
6889     if defined $token->{line};
6890     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
6891     if defined $token->{column};
6892    
6893 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
6894 wakaba 1.136 $self->{insertion_mode} = AFTER_HEAD_IM;
6895    
6896     ## Reprocess in the "after head" insertion mode...
6897     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6898    
6899     ## As if </head>
6900     pop @{$self->{open_elements}};
6901     $self->{insertion_mode} = AFTER_HEAD_IM;
6902    
6903     ## Reprocess in the "after head" insertion mode...
6904     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6905    
6906     ## ISSUE: Two parse errors for <head><noscript></br>
6907 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6908     text => 'br', token => $token);
6909 wakaba 1.136 ## As if </noscript>
6910     pop @{$self->{open_elements}};
6911     $self->{insertion_mode} = IN_HEAD_IM;
6912 wakaba 1.54
6913     ## Reprocess in the "in head" insertion mode...
6914 wakaba 1.136 ## As if </head>
6915     pop @{$self->{open_elements}};
6916     $self->{insertion_mode} = AFTER_HEAD_IM;
6917 wakaba 1.54
6918 wakaba 1.136 ## Reprocess in the "after head" insertion mode...
6919     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
6920 wakaba 1.79
6921 wakaba 1.56 #
6922     } else {
6923 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
6924 wakaba 1.54 }
6925 wakaba 1.136
6926     ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
6927 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6928     text => 'br', token => $token);
6929 wakaba 1.136 ## Ignore the token
6930     $token = $self->_get_next_token;
6931     next B;
6932     } else {
6933    
6934 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6935     text => $token->{tag_name}, token => $token);
6936 wakaba 1.136 ## Ignore the token
6937     $token = $self->_get_next_token;
6938     next B;
6939 wakaba 1.54 }
6940    
6941 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
6942 wakaba 1.79
6943 wakaba 1.54 ## As if </noscript>
6944     pop @{$self->{open_elements}};
6945 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:/',
6946     text => $token->{tag_name}, token => $token);
6947 wakaba 1.54
6948     ## Reprocess in the "in head" insertion mode...
6949     ## As if </head>
6950     pop @{$self->{open_elements}};
6951    
6952     ## Reprocess in the "after head" insertion mode...
6953 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
6954 wakaba 1.79
6955 wakaba 1.54 ## As if </head>
6956     pop @{$self->{open_elements}};
6957    
6958     ## Reprocess in the "after head" insertion mode...
6959 wakaba 1.56 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6960 wakaba 1.82 ## ISSUE: This case cannot be reached?
6961 wakaba 1.79
6962 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
6963     text => $token->{tag_name}, token => $token);
6964 wakaba 1.54 ## Ignore the token ## ISSUE: An issue in the spec.
6965     $token = $self->_get_next_token;
6966 wakaba 1.123 next B;
6967 wakaba 1.79 } else {
6968    
6969 wakaba 1.8 }
6970 wakaba 1.54
6971     ## "after head" insertion mode
6972     ## As if <body>
6973    
6974 wakaba 1.1 {
6975     my $el;
6976    
6977     $el = $self->{document}->create_element_ns
6978 wakaba 1.128 ($HTML_NS, [undef, 'body']);
6979 wakaba 1.1
6980 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6981     if defined $token->{line};
6982     $el->set_user_data (manakai_source_column => $token->{column})
6983     if defined $token->{column};
6984    
6985 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6986 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
6987 wakaba 1.1 }
6988    
6989 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
6990 wakaba 1.54 ## reprocess
6991 wakaba 1.123 next B;
6992 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6993     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
6994    
6995    
6996     ## NOTE: As if <head>
6997    
6998     $self->{head_element} = $self->{document}->create_element_ns
6999 wakaba 1.128 ($HTML_NS, [undef, 'head']);
7000 wakaba 1.103
7001 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
7002     if defined $token->{line};
7003     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
7004     if defined $token->{column};
7005    
7006 wakaba 1.103 $self->{open_elements}->[-1]->[0]->append_child
7007     ($self->{head_element});
7008 wakaba 1.121 #push @{$self->{open_elements}},
7009     # [$self->{head_element}, $el_category->{head}];
7010 wakaba 1.103 #$self->{insertion_mode} = IN_HEAD_IM;
7011     ## NOTE: Reprocess.
7012    
7013     ## NOTE: As if </head>
7014     #pop @{$self->{open_elements}};
7015     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
7016     ## NOTE: Reprocess.
7017    
7018     #
7019     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
7020    
7021    
7022     ## NOTE: As if </head>
7023     pop @{$self->{open_elements}};
7024     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
7025     ## NOTE: Reprocess.
7026    
7027     #
7028     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
7029    
7030    
7031 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in noscript:#eof', token => $token);
7032 wakaba 1.103
7033     ## As if </noscript>
7034     pop @{$self->{open_elements}};
7035     #$self->{insertion_mode} = IN_HEAD_IM;
7036     ## NOTE: Reprocess.
7037    
7038     ## NOTE: As if </head>
7039     pop @{$self->{open_elements}};
7040     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
7041     ## NOTE: Reprocess.
7042    
7043     #
7044     } else {
7045    
7046     #
7047     }
7048    
7049     ## NOTE: As if <body>
7050    
7051     {
7052     my $el;
7053    
7054     $el = $self->{document}->create_element_ns
7055 wakaba 1.128 ($HTML_NS, [undef, 'body']);
7056 wakaba 1.103
7057 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7058     if defined $token->{line};
7059     $el->set_user_data (manakai_source_column => $token->{column})
7060     if defined $token->{column};
7061    
7062 wakaba 1.103 $self->{open_elements}->[-1]->[0]->append_child ($el);
7063 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
7064 wakaba 1.103 }
7065    
7066     $self->{insertion_mode} = IN_BODY_IM;
7067     ## NOTE: Reprocess.
7068 wakaba 1.123 next B;
7069 wakaba 1.103 } else {
7070     die "$0: $token->{type}: Unknown token type";
7071     }
7072 wakaba 1.58 } elsif ($self->{insertion_mode} & BODY_IMS) {
7073 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
7074 wakaba 1.79
7075 wakaba 1.54 ## NOTE: There is a code clone of "character in body".
7076     $reconstruct_active_formatting_elements->($insert_to_current);
7077    
7078     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
7079    
7080     $token = $self->_get_next_token;
7081 wakaba 1.123 next B;
7082 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
7083 wakaba 1.54 if ({
7084     caption => 1, col => 1, colgroup => 1, tbody => 1,
7085     td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
7086     }->{$token->{tag_name}}) {
7087 wakaba 1.56 if ($self->{insertion_mode} == IN_CELL_IM) {
7088 wakaba 1.54 ## have an element in table scope
7089 wakaba 1.106 for (reverse 0..$#{$self->{open_elements}}) {
7090 wakaba 1.54 my $node = $self->{open_elements}->[$_];
7091 wakaba 1.121 if ($node->[1] & TABLE_CELL_EL) {
7092 wakaba 1.79
7093 wakaba 1.106
7094     ## Close the cell
7095 wakaba 1.122
7096     $token->{self_closing} = $self->{self_closing};
7097     unshift @{$self->{token}}, $token;
7098     delete $self->{self_closing};
7099     # <x>
7100 wakaba 1.120 $token = {type => END_TAG_TOKEN,
7101     tag_name => $node->[0]->manakai_local_name,
7102 wakaba 1.112 line => $token->{line},
7103     column => $token->{column}};
7104 wakaba 1.123 next B;
7105 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7106 wakaba 1.79
7107 wakaba 1.106 ## ISSUE: This case can never be reached, maybe.
7108     last;
7109 wakaba 1.54 }
7110 wakaba 1.106 }
7111    
7112 wakaba 1.54
7113 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'start tag not allowed',
7114     text => $token->{tag_name}, token => $token);
7115 wakaba 1.106 ## Ignore the token
7116 wakaba 1.122
7117 wakaba 1.106 $token = $self->_get_next_token;
7118 wakaba 1.123 next B;
7119 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
7120 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'caption',
7121     token => $token);
7122 wakaba 1.54
7123 wakaba 1.106 ## NOTE: As if </caption>.
7124 wakaba 1.54 ## have a table element in table scope
7125     my $i;
7126 wakaba 1.106 INSCOPE: {
7127     for (reverse 0..$#{$self->{open_elements}}) {
7128     my $node = $self->{open_elements}->[$_];
7129 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
7130 wakaba 1.106
7131     $i = $_;
7132     last INSCOPE;
7133 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7134 wakaba 1.106
7135     last;
7136     }
7137 wakaba 1.54 }
7138 wakaba 1.106
7139    
7140 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'start tag not allowed',
7141     text => $token->{tag_name}, token => $token);
7142 wakaba 1.106 ## Ignore the token
7143 wakaba 1.122
7144 wakaba 1.106 $token = $self->_get_next_token;
7145 wakaba 1.123 next B;
7146 wakaba 1.54 } # INSCOPE
7147    
7148     ## generate implied end tags
7149 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7150     & END_TAG_OPTIONAL_EL) {
7151 wakaba 1.79
7152 wakaba 1.86 pop @{$self->{open_elements}};
7153 wakaba 1.54 }
7154    
7155 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7156 wakaba 1.79
7157 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7158     text => $self->{open_elements}->[-1]->[0]
7159 wakaba 1.120 ->manakai_local_name,
7160     token => $token);
7161 wakaba 1.79 } else {
7162    
7163 wakaba 1.54 }
7164    
7165     splice @{$self->{open_elements}}, $i;
7166    
7167     $clear_up_to_marker->();
7168    
7169 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7170 wakaba 1.54
7171     ## reprocess
7172 wakaba 1.122
7173 wakaba 1.123 next B;
7174 wakaba 1.54 } else {
7175 wakaba 1.79
7176 wakaba 1.54 #
7177     }
7178     } else {
7179 wakaba 1.79
7180 wakaba 1.54 #
7181     }
7182 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
7183 wakaba 1.54 if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
7184 wakaba 1.56 if ($self->{insertion_mode} == IN_CELL_IM) {
7185 wakaba 1.54 ## have an element in table scope
7186     my $i;
7187     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7188     my $node = $self->{open_elements}->[$_];
7189 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7190 wakaba 1.79
7191 wakaba 1.54 $i = $_;
7192     last INSCOPE;
7193 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7194 wakaba 1.79
7195 wakaba 1.54 last INSCOPE;
7196     }
7197     } # INSCOPE
7198     unless (defined $i) {
7199 wakaba 1.79
7200 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7201     text => $token->{tag_name},
7202     token => $token);
7203 wakaba 1.54 ## Ignore the token
7204     $token = $self->_get_next_token;
7205 wakaba 1.123 next B;
7206 wakaba 1.54 }
7207    
7208     ## generate implied end tags
7209 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7210     & END_TAG_OPTIONAL_EL) {
7211 wakaba 1.79
7212 wakaba 1.86 pop @{$self->{open_elements}};
7213 wakaba 1.54 }
7214 wakaba 1.86
7215 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7216     ne $token->{tag_name}) {
7217 wakaba 1.79
7218 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7219     text => $self->{open_elements}->[-1]->[0]
7220 wakaba 1.120 ->manakai_local_name,
7221     token => $token);
7222 wakaba 1.79 } else {
7223    
7224 wakaba 1.54 }
7225    
7226     splice @{$self->{open_elements}}, $i;
7227    
7228     $clear_up_to_marker->();
7229    
7230 wakaba 1.56 $self->{insertion_mode} = IN_ROW_IM;
7231 wakaba 1.54
7232     $token = $self->_get_next_token;
7233 wakaba 1.123 next B;
7234 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
7235 wakaba 1.79
7236 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7237     text => $token->{tag_name}, token => $token);
7238 wakaba 1.54 ## Ignore the token
7239     $token = $self->_get_next_token;
7240 wakaba 1.123 next B;
7241 wakaba 1.54 } else {
7242 wakaba 1.79
7243 wakaba 1.54 #
7244     }
7245     } elsif ($token->{tag_name} eq 'caption') {
7246 wakaba 1.56 if ($self->{insertion_mode} == IN_CAPTION_IM) {
7247 wakaba 1.54 ## have a table element in table scope
7248     my $i;
7249 wakaba 1.106 INSCOPE: {
7250     for (reverse 0..$#{$self->{open_elements}}) {
7251     my $node = $self->{open_elements}->[$_];
7252 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
7253 wakaba 1.106
7254     $i = $_;
7255     last INSCOPE;
7256 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7257 wakaba 1.106
7258     last;
7259     }
7260 wakaba 1.54 }
7261 wakaba 1.106
7262    
7263 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7264     text => $token->{tag_name}, token => $token);
7265 wakaba 1.106 ## Ignore the token
7266     $token = $self->_get_next_token;
7267 wakaba 1.123 next B;
7268 wakaba 1.54 } # INSCOPE
7269    
7270     ## generate implied end tags
7271 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
7272     & END_TAG_OPTIONAL_EL) {
7273 wakaba 1.79
7274 wakaba 1.86 pop @{$self->{open_elements}};
7275 wakaba 1.54 }
7276    
7277 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7278 wakaba 1.79
7279 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7280     text => $self->{open_elements}->[-1]->[0]
7281 wakaba 1.120 ->manakai_local_name,
7282     token => $token);
7283 wakaba 1.79 } else {
7284    
7285 wakaba 1.54 }
7286    
7287     splice @{$self->{open_elements}}, $i;
7288    
7289     $clear_up_to_marker->();
7290    
7291 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7292 wakaba 1.54
7293     $token = $self->_get_next_token;
7294 wakaba 1.123 next B;
7295 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CELL_IM) {
7296 wakaba 1.79
7297 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7298     text => $token->{tag_name}, token => $token);
7299 wakaba 1.54 ## Ignore the token
7300     $token = $self->_get_next_token;
7301 wakaba 1.123 next B;
7302 wakaba 1.54 } else {
7303 wakaba 1.79
7304 wakaba 1.54 #
7305 wakaba 1.1 }
7306 wakaba 1.54 } elsif ({
7307     table => 1, tbody => 1, tfoot => 1,
7308     thead => 1, tr => 1,
7309     }->{$token->{tag_name}} and
7310 wakaba 1.56 $self->{insertion_mode} == IN_CELL_IM) {
7311 wakaba 1.54 ## have an element in table scope
7312     my $i;
7313     my $tn;
7314 wakaba 1.106 INSCOPE: {
7315     for (reverse 0..$#{$self->{open_elements}}) {
7316     my $node = $self->{open_elements}->[$_];
7317 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7318 wakaba 1.106
7319     $i = $_;
7320    
7321     ## Close the cell
7322 wakaba 1.122
7323     $token->{self_closing} = $self->{self_closing};
7324     unshift @{$self->{token}}, $token;
7325     delete $self->{self_closing};
7326     # </x>
7327 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => $tn,
7328     line => $token->{line},
7329     column => $token->{column}};
7330 wakaba 1.123 next B;
7331 wakaba 1.121 } elsif ($node->[1] & TABLE_CELL_EL) {
7332 wakaba 1.106
7333 wakaba 1.121 $tn = $node->[0]->manakai_local_name;
7334 wakaba 1.106 ## NOTE: There is exactly one |td| or |th| element
7335     ## in scope in the stack of open elements by definition.
7336 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7337 wakaba 1.106 ## ISSUE: Can this be reached?
7338    
7339     last;
7340     }
7341 wakaba 1.54 }
7342 wakaba 1.106
7343 wakaba 1.79
7344 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7345     text => $token->{tag_name}, token => $token);
7346 wakaba 1.54 ## Ignore the token
7347     $token = $self->_get_next_token;
7348 wakaba 1.123 next B;
7349 wakaba 1.106 } # INSCOPE
7350 wakaba 1.54 } elsif ($token->{tag_name} eq 'table' and
7351 wakaba 1.56 $self->{insertion_mode} == IN_CAPTION_IM) {
7352 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'caption',
7353     token => $token);
7354 wakaba 1.31
7355 wakaba 1.54 ## As if </caption>
7356     ## have a table element in table scope
7357     my $i;
7358     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7359     my $node = $self->{open_elements}->[$_];
7360 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
7361 wakaba 1.79
7362 wakaba 1.54 $i = $_;
7363     last INSCOPE;
7364 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7365 wakaba 1.79
7366 wakaba 1.54 last INSCOPE;
7367     }
7368     } # INSCOPE
7369     unless (defined $i) {
7370 wakaba 1.79
7371 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7372     text => 'caption', token => $token);
7373 wakaba 1.54 ## Ignore the token
7374     $token = $self->_get_next_token;
7375 wakaba 1.123 next B;
7376 wakaba 1.54 }
7377    
7378     ## generate implied end tags
7379 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7380 wakaba 1.79
7381 wakaba 1.86 pop @{$self->{open_elements}};
7382 wakaba 1.54 }
7383 wakaba 1.20
7384 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
7385 wakaba 1.79
7386 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7387     text => $self->{open_elements}->[-1]->[0]
7388 wakaba 1.120 ->manakai_local_name,
7389     token => $token);
7390 wakaba 1.79 } else {
7391    
7392 wakaba 1.54 }
7393 wakaba 1.12
7394 wakaba 1.54 splice @{$self->{open_elements}}, $i;
7395 wakaba 1.31
7396 wakaba 1.54 $clear_up_to_marker->();
7397 wakaba 1.1
7398 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7399 wakaba 1.3
7400 wakaba 1.54 ## reprocess
7401 wakaba 1.123 next B;
7402 wakaba 1.54 } elsif ({
7403     body => 1, col => 1, colgroup => 1, html => 1,
7404     }->{$token->{tag_name}}) {
7405 wakaba 1.58 if ($self->{insertion_mode} & BODY_TABLE_IMS) {
7406 wakaba 1.79
7407 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7408     text => $token->{tag_name}, token => $token);
7409 wakaba 1.54 ## Ignore the token
7410     $token = $self->_get_next_token;
7411 wakaba 1.123 next B;
7412 wakaba 1.54 } else {
7413 wakaba 1.79
7414 wakaba 1.54 #
7415     }
7416     } elsif ({
7417     tbody => 1, tfoot => 1,
7418     thead => 1, tr => 1,
7419     }->{$token->{tag_name}} and
7420 wakaba 1.56 $self->{insertion_mode} == IN_CAPTION_IM) {
7421 wakaba 1.79
7422 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7423     text => $token->{tag_name}, token => $token);
7424 wakaba 1.1 ## Ignore the token
7425     $token = $self->_get_next_token;
7426 wakaba 1.123 next B;
7427 wakaba 1.54 } else {
7428 wakaba 1.79
7429 wakaba 1.54 #
7430 wakaba 1.1 }
7431 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7432     for my $entry (@{$self->{open_elements}}) {
7433 wakaba 1.121 unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
7434 wakaba 1.103
7435 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
7436 wakaba 1.103 last;
7437     }
7438     }
7439    
7440     ## Stop parsing.
7441     last B;
7442 wakaba 1.54 } else {
7443     die "$0: $token->{type}: Unknown token type";
7444 wakaba 1.1 }
7445    
7446 wakaba 1.54 $insert = $insert_to_current;
7447     #
7448 wakaba 1.58 } elsif ($self->{insertion_mode} & TABLE_IMS) {
7449 wakaba 1.60 if ($token->{type} == CHARACTER_TOKEN) {
7450 wakaba 1.95 if (not $open_tables->[-1]->[1] and # tainted
7451 wakaba 1.184 $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
7452 wakaba 1.95 $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
7453 wakaba 1.54
7454 wakaba 1.95 unless (length $token->{data}) {
7455    
7456     $token = $self->_get_next_token;
7457 wakaba 1.123 next B;
7458 wakaba 1.95 } else {
7459    
7460     }
7461     }
7462 wakaba 1.1
7463 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table:#text', token => $token);
7464 wakaba 1.1
7465 wakaba 1.197 ## NOTE: As if in body, but insert into the foster parent element.
7466     $reconstruct_active_formatting_elements->($insert_to_foster);
7467    
7468     if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
7469     # MUST
7470     my $foster_parent_element;
7471     my $next_sibling;
7472     my $prev_sibling;
7473     OE: for (reverse 0..$#{$self->{open_elements}}) {
7474     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
7475     my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
7476     if (defined $parent and $parent->node_type == 1) {
7477     $foster_parent_element = $parent;
7478 wakaba 1.79
7479 wakaba 1.197 $next_sibling = $self->{open_elements}->[$_]->[0];
7480     $prev_sibling = $next_sibling->previous_sibling;
7481     #
7482 wakaba 1.54 } else {
7483 wakaba 1.79
7484 wakaba 1.197 $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
7485     $prev_sibling = $foster_parent_element->last_child;
7486     #
7487 wakaba 1.54 }
7488 wakaba 1.197 last OE;
7489     }
7490     } # OE
7491     $foster_parent_element = $self->{open_elements}->[0]->[0] and
7492     $prev_sibling = $foster_parent_element->last_child
7493     unless defined $foster_parent_element;
7494     undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
7495     if (defined $prev_sibling and
7496     $prev_sibling->node_type == 3) {
7497    
7498     $prev_sibling->manakai_append_text ($token->{data});
7499     } else {
7500    
7501     $foster_parent_element->insert_before
7502     ($self->{document}->create_text_node ($token->{data}),
7503     $next_sibling);
7504     }
7505 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
7506 wakaba 1.197 $open_tables->[-1]->[2] = 1; # ~node inserted
7507 wakaba 1.95 } else {
7508 wakaba 1.197 ## NOTE: Fragment case or in a foster parent'ed element
7509     ## (e.g. |<table><span>a|). In fragment case, whether the
7510     ## character is appended to existing node or a new node is
7511     ## created is irrelevant, since the foster parent'ed nodes
7512     ## are discarded and fragment parsing does not invoke any
7513     ## script.
7514 wakaba 1.95
7515 wakaba 1.197 $self->{open_elements}->[-1]->[0]->manakai_append_text
7516     ($token->{data});
7517 wakaba 1.95 }
7518 wakaba 1.54
7519 wakaba 1.95 $token = $self->_get_next_token;
7520 wakaba 1.123 next B;
7521 wakaba 1.60 } elsif ($token->{type} == START_TAG_TOKEN) {
7522 wakaba 1.150 if ({
7523     tr => ($self->{insertion_mode} != IN_ROW_IM),
7524     th => 1, td => 1,
7525     }->{$token->{tag_name}}) {
7526     if ($self->{insertion_mode} == IN_TABLE_IM) {
7527     ## Clear back to table context
7528     while (not ($self->{open_elements}->[-1]->[1]
7529     & TABLE_SCOPING_EL)) {
7530    
7531     pop @{$self->{open_elements}};
7532     }
7533    
7534    
7535 wakaba 1.54 {
7536     my $el;
7537    
7538     $el = $self->{document}->create_element_ns
7539 wakaba 1.128 ($HTML_NS, [undef, 'tbody']);
7540 wakaba 1.54
7541 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7542     if defined $token->{line};
7543     $el->set_user_data (manakai_source_column => $token->{column})
7544     if defined $token->{column};
7545    
7546 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7547 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'tbody'} || 0];
7548 wakaba 1.54 }
7549    
7550 wakaba 1.150 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7551     ## reprocess in the "in table body" insertion mode...
7552     }
7553    
7554     if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
7555     unless ($token->{tag_name} eq 'tr') {
7556    
7557     $self->{parse_error}->(level => $self->{level}->{must}, type => 'missing start tag:tr', token => $token);
7558     }
7559 wakaba 1.54
7560 wakaba 1.150 ## Clear back to table body context
7561     while (not ($self->{open_elements}->[-1]->[1]
7562     & TABLE_ROWS_SCOPING_EL)) {
7563    
7564     ## ISSUE: Can this case be reached?
7565     pop @{$self->{open_elements}};
7566     }
7567 wakaba 1.54
7568 wakaba 1.197 $self->{insertion_mode} = IN_ROW_IM;
7569     if ($token->{tag_name} eq 'tr') {
7570    
7571    
7572 wakaba 1.54 {
7573     my $el;
7574    
7575     $el = $self->{document}->create_element_ns
7576 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7577 wakaba 1.54
7578     for my $attr_name (keys %{ $token->{attributes}}) {
7579 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7580 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7581 wakaba 1.117 $attr->value ($attr_t->{value});
7582     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7583     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7584     $el->set_attribute_node_ns ($attr);
7585 wakaba 1.1 }
7586 wakaba 1.54
7587 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7588     if defined $token->{line};
7589     $el->set_user_data (manakai_source_column => $token->{column})
7590     if defined $token->{column};
7591    
7592 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7593 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7594 wakaba 1.54 }
7595    
7596 wakaba 1.197 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
7597    
7598     $token = $self->_get_next_token;
7599     next B;
7600     } else {
7601    
7602    
7603 wakaba 1.54 {
7604     my $el;
7605    
7606     $el = $self->{document}->create_element_ns
7607 wakaba 1.128 ($HTML_NS, [undef, 'tr']);
7608 wakaba 1.54
7609 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7610     if defined $token->{line};
7611     $el->set_user_data (manakai_source_column => $token->{column})
7612     if defined $token->{column};
7613    
7614 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7615 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'tr'} || 0];
7616 wakaba 1.54 }
7617    
7618 wakaba 1.197 ## reprocess in the "in row" insertion mode
7619     }
7620     } else {
7621    
7622     }
7623 wakaba 1.52
7624 wakaba 1.54 ## Clear back to table row context
7625 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7626     & TABLE_ROW_SCOPING_EL)) {
7627 wakaba 1.79
7628 wakaba 1.54 pop @{$self->{open_elements}};
7629     }
7630    
7631 wakaba 1.197
7632 wakaba 1.54 {
7633     my $el;
7634    
7635     $el = $self->{document}->create_element_ns
7636 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7637 wakaba 1.1
7638 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
7639 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7640 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7641 wakaba 1.117 $attr->value ($attr_t->{value});
7642     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7643     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7644     $el->set_attribute_node_ns ($attr);
7645 wakaba 1.54 }
7646    
7647 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7648     if defined $token->{line};
7649     $el->set_user_data (manakai_source_column => $token->{column})
7650     if defined $token->{column};
7651    
7652 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7653 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7654 wakaba 1.54 }
7655    
7656 wakaba 1.197 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
7657     $self->{insertion_mode} = IN_CELL_IM;
7658 wakaba 1.54
7659 wakaba 1.197 push @$active_formatting_elements, ['#marker', ''];
7660 wakaba 1.54
7661 wakaba 1.197
7662     $token = $self->_get_next_token;
7663     next B;
7664     } elsif ({
7665     caption => 1, col => 1, colgroup => 1,
7666     tbody => 1, tfoot => 1, thead => 1,
7667     tr => 1, # $self->{insertion_mode} == IN_ROW_IM
7668     }->{$token->{tag_name}}) {
7669     if ($self->{insertion_mode} == IN_ROW_IM) {
7670     ## As if </tr>
7671     ## have an element in table scope
7672     my $i;
7673     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7674     my $node = $self->{open_elements}->[$_];
7675     if ($node->[1] & TABLE_ROW_EL) {
7676    
7677     $i = $_;
7678     last INSCOPE;
7679     } elsif ($node->[1] & TABLE_SCOPING_EL) {
7680    
7681     last INSCOPE;
7682     }
7683     } # INSCOPE
7684     unless (defined $i) {
7685    
7686     ## TODO: This type is wrong.
7687     $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmacthed end tag',
7688     text => $token->{tag_name}, token => $token);
7689     ## Ignore the token
7690 wakaba 1.122
7691 wakaba 1.54 $token = $self->_get_next_token;
7692 wakaba 1.123 next B;
7693 wakaba 1.197 }
7694 wakaba 1.54
7695     ## Clear back to table row context
7696 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7697     & TABLE_ROW_SCOPING_EL)) {
7698 wakaba 1.79
7699 wakaba 1.83 ## ISSUE: Can this case be reached?
7700 wakaba 1.54 pop @{$self->{open_elements}};
7701     }
7702    
7703     pop @{$self->{open_elements}}; # tr
7704 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7705 wakaba 1.54 if ($token->{tag_name} eq 'tr') {
7706 wakaba 1.79
7707 wakaba 1.54 ## reprocess
7708 wakaba 1.122
7709 wakaba 1.123 next B;
7710 wakaba 1.54 } else {
7711 wakaba 1.79
7712 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
7713     }
7714     }
7715 wakaba 1.52
7716 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
7717 wakaba 1.54 ## have an element in table scope
7718     my $i;
7719     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7720     my $node = $self->{open_elements}->[$_];
7721 wakaba 1.121 if ($node->[1] & TABLE_ROW_GROUP_EL) {
7722 wakaba 1.79
7723 wakaba 1.54 $i = $_;
7724     last INSCOPE;
7725 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7726 wakaba 1.79
7727 wakaba 1.54 last INSCOPE;
7728     }
7729     } # INSCOPE
7730     unless (defined $i) {
7731 wakaba 1.79
7732 wakaba 1.150 ## TODO: This erorr type is wrong.
7733     $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
7734     text => $token->{tag_name}, token => $token);
7735 wakaba 1.54 ## Ignore the token
7736 wakaba 1.122
7737 wakaba 1.54 $token = $self->_get_next_token;
7738 wakaba 1.123 next B;
7739 wakaba 1.54 }
7740 wakaba 1.52
7741 wakaba 1.54 ## Clear back to table body context
7742 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7743     & TABLE_ROWS_SCOPING_EL)) {
7744 wakaba 1.79
7745 wakaba 1.83 ## ISSUE: Can this state be reached?
7746 wakaba 1.54 pop @{$self->{open_elements}};
7747     }
7748    
7749     ## As if <{current node}>
7750     ## have an element in table scope
7751     ## true by definition
7752    
7753     ## Clear back to table body context
7754     ## nop by definition
7755    
7756     pop @{$self->{open_elements}};
7757 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7758 wakaba 1.54 ## reprocess in "in table" insertion mode...
7759 wakaba 1.79 } else {
7760    
7761 wakaba 1.54 }
7762 wakaba 1.51
7763 wakaba 1.197 if ($token->{tag_name} eq 'col') {
7764     ## Clear back to table context
7765     while (not ($self->{open_elements}->[-1]->[1]
7766     & TABLE_SCOPING_EL)) {
7767    
7768     ## ISSUE: Can this state be reached?
7769     pop @{$self->{open_elements}};
7770     }
7771    
7772    
7773 wakaba 1.51 {
7774     my $el;
7775    
7776     $el = $self->{document}->create_element_ns
7777 wakaba 1.128 ($HTML_NS, [undef, 'colgroup']);
7778 wakaba 1.51
7779 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7780     if defined $token->{line};
7781     $el->set_user_data (manakai_source_column => $token->{column})
7782     if defined $token->{column};
7783    
7784 wakaba 1.51 $self->{open_elements}->[-1]->[0]->append_child ($el);
7785 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'colgroup'} || 0];
7786 wakaba 1.51 }
7787    
7788 wakaba 1.197 $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
7789     ## reprocess
7790     $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
7791    
7792     next B;
7793     } elsif ({
7794     caption => 1,
7795     colgroup => 1,
7796     tbody => 1, tfoot => 1, thead => 1,
7797     }->{$token->{tag_name}}) {
7798     ## Clear back to table context
7799 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7800     & TABLE_SCOPING_EL)) {
7801 wakaba 1.79
7802 wakaba 1.83 ## ISSUE: Can this state be reached?
7803 wakaba 1.54 pop @{$self->{open_elements}};
7804     }
7805    
7806 wakaba 1.197 push @$active_formatting_elements, ['#marker', '']
7807     if $token->{tag_name} eq 'caption';
7808 wakaba 1.52
7809 wakaba 1.197
7810 wakaba 1.54 {
7811     my $el;
7812    
7813     $el = $self->{document}->create_element_ns
7814 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7815 wakaba 1.52
7816 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
7817 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7818 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7819 wakaba 1.117 $attr->value ($attr_t->{value});
7820     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7821     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7822     $el->set_attribute_node_ns ($attr);
7823 wakaba 1.52 }
7824    
7825 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7826     if defined $token->{line};
7827     $el->set_user_data (manakai_source_column => $token->{column})
7828     if defined $token->{column};
7829    
7830 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7831 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7832 wakaba 1.54 }
7833    
7834 wakaba 1.197 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
7835     $self->{insertion_mode} = {
7836     caption => IN_CAPTION_IM,
7837     colgroup => IN_COLUMN_GROUP_IM,
7838     tbody => IN_TABLE_BODY_IM,
7839     tfoot => IN_TABLE_BODY_IM,
7840     thead => IN_TABLE_BODY_IM,
7841     }->{$token->{tag_name}};
7842     $token = $self->_get_next_token;
7843    
7844     next B;
7845     } else {
7846     die "$0: in table: <>: $token->{tag_name}";
7847     }
7848 wakaba 1.54 } elsif ($token->{tag_name} eq 'table') {
7849 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7850     text => $self->{open_elements}->[-1]->[0]
7851 wakaba 1.120 ->manakai_local_name,
7852     token => $token);
7853 wakaba 1.54
7854     ## As if </table>
7855     ## have a table element in table scope
7856     my $i;
7857     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7858     my $node = $self->{open_elements}->[$_];
7859 wakaba 1.121 if ($node->[1] & TABLE_EL) {
7860 wakaba 1.79
7861 wakaba 1.54 $i = $_;
7862     last INSCOPE;
7863 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7864 wakaba 1.79
7865 wakaba 1.54 last INSCOPE;
7866     }
7867     } # INSCOPE
7868     unless (defined $i) {
7869 wakaba 1.79
7870 wakaba 1.83 ## TODO: The following is wrong, maybe.
7871 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag', text => 'table',
7872     token => $token);
7873 wakaba 1.54 ## Ignore tokens </table><table>
7874 wakaba 1.122
7875 wakaba 1.52 $token = $self->_get_next_token;
7876 wakaba 1.123 next B;
7877 wakaba 1.52 }
7878    
7879 wakaba 1.149 ## TODO: Followings are removed from the latest spec.
7880 wakaba 1.54 ## generate implied end tags
7881 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7882 wakaba 1.79
7883 wakaba 1.86 pop @{$self->{open_elements}};
7884 wakaba 1.54 }
7885    
7886 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
7887 wakaba 1.79
7888 wakaba 1.120 ## NOTE: |<table><tr><table>|
7889 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
7890     text => $self->{open_elements}->[-1]->[0]
7891 wakaba 1.120 ->manakai_local_name,
7892     token => $token);
7893 wakaba 1.79 } else {
7894    
7895 wakaba 1.54 }
7896    
7897     splice @{$self->{open_elements}}, $i;
7898 wakaba 1.95 pop @{$open_tables};
7899 wakaba 1.54
7900     $self->_reset_insertion_mode;
7901 wakaba 1.52
7902 wakaba 1.122 ## reprocess
7903    
7904 wakaba 1.123 next B;
7905 wakaba 1.100 } elsif ($token->{tag_name} eq 'style') {
7906     if (not $open_tables->[-1]->[1]) { # tainted
7907    
7908     ## NOTE: This is a "as if in head" code clone.
7909     $parse_rcdata->(CDATA_CONTENT_MODEL);
7910 wakaba 1.197 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
7911 wakaba 1.123 next B;
7912 wakaba 1.100 } else {
7913    
7914     #
7915     }
7916     } elsif ($token->{tag_name} eq 'script') {
7917     if (not $open_tables->[-1]->[1]) { # tainted
7918    
7919     ## NOTE: This is a "as if in head" code clone.
7920     $script_start_tag->();
7921 wakaba 1.197 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
7922 wakaba 1.123 next B;
7923 wakaba 1.100 } else {
7924    
7925     #
7926     }
7927 wakaba 1.98 } elsif ($token->{tag_name} eq 'input') {
7928     if (not $open_tables->[-1]->[1]) { # tainted
7929     if ($token->{attributes}->{type}) { ## TODO: case
7930     my $type = lc $token->{attributes}->{type}->{value};
7931     if ($type eq 'hidden') {
7932    
7933 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table',
7934     text => $token->{tag_name}, token => $token);
7935 wakaba 1.98
7936    
7937     {
7938     my $el;
7939    
7940     $el = $self->{document}->create_element_ns
7941 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7942 wakaba 1.98
7943     for my $attr_name (keys %{ $token->{attributes}}) {
7944 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7945 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7946 wakaba 1.117 $attr->value ($attr_t->{value});
7947     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7948     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7949     $el->set_attribute_node_ns ($attr);
7950 wakaba 1.98 }
7951    
7952 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7953     if defined $token->{line};
7954     $el->set_user_data (manakai_source_column => $token->{column})
7955     if defined $token->{column};
7956    
7957 wakaba 1.98 $self->{open_elements}->[-1]->[0]->append_child ($el);
7958 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7959 wakaba 1.98 }
7960    
7961 wakaba 1.197 $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
7962 wakaba 1.98
7963     ## TODO: form element pointer
7964    
7965     pop @{$self->{open_elements}};
7966    
7967     $token = $self->_get_next_token;
7968 wakaba 1.122 delete $self->{self_closing};
7969 wakaba 1.123 next B;
7970 wakaba 1.98 } else {
7971    
7972     #
7973     }
7974     } else {
7975    
7976     #
7977     }
7978     } else {
7979    
7980     #
7981     }
7982 wakaba 1.60 } else {
7983 wakaba 1.79
7984 wakaba 1.60 #
7985     }
7986 wakaba 1.98
7987 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table', text => $token->{tag_name},
7988     token => $token);
7989 wakaba 1.98
7990     $insert = $insert_to_foster;
7991     #
7992 wakaba 1.60 } elsif ($token->{type} == END_TAG_TOKEN) {
7993 wakaba 1.54 if ($token->{tag_name} eq 'tr' and
7994 wakaba 1.56 $self->{insertion_mode} == IN_ROW_IM) {
7995 wakaba 1.54 ## have an element in table scope
7996     my $i;
7997     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7998     my $node = $self->{open_elements}->[$_];
7999 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
8000 wakaba 1.79
8001 wakaba 1.54 $i = $_;
8002     last INSCOPE;
8003 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8004 wakaba 1.79
8005 wakaba 1.54 last INSCOPE;
8006     }
8007     } # INSCOPE
8008     unless (defined $i) {
8009 wakaba 1.79
8010 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8011     text => $token->{tag_name}, token => $token);
8012 wakaba 1.54 ## Ignore the token
8013 wakaba 1.122
8014 wakaba 1.54 $token = $self->_get_next_token;
8015 wakaba 1.123 next B;
8016 wakaba 1.79 } else {
8017    
8018 wakaba 1.54 }
8019    
8020     ## Clear back to table row context
8021 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8022     & TABLE_ROW_SCOPING_EL)) {
8023 wakaba 1.79
8024 wakaba 1.83 ## ISSUE: Can this state be reached?
8025 wakaba 1.54 pop @{$self->{open_elements}};
8026     }
8027    
8028     pop @{$self->{open_elements}}; # tr
8029 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
8030 wakaba 1.54 $token = $self->_get_next_token;
8031 wakaba 1.122
8032 wakaba 1.123 next B;
8033 wakaba 1.54 } elsif ($token->{tag_name} eq 'table') {
8034 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
8035 wakaba 1.54 ## As if </tr>
8036     ## have an element in table scope
8037     my $i;
8038     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8039     my $node = $self->{open_elements}->[$_];
8040 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
8041 wakaba 1.79
8042 wakaba 1.54 $i = $_;
8043     last INSCOPE;
8044 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8045 wakaba 1.79
8046 wakaba 1.54 last INSCOPE;
8047     }
8048     } # INSCOPE
8049     unless (defined $i) {
8050 wakaba 1.79
8051 wakaba 1.83 ## TODO: The following is wrong.
8052 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8053     text => $token->{type}, token => $token);
8054 wakaba 1.54 ## Ignore the token
8055 wakaba 1.122
8056 wakaba 1.54 $token = $self->_get_next_token;
8057 wakaba 1.123 next B;
8058 wakaba 1.54 }
8059    
8060     ## Clear back to table row context
8061 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8062     & TABLE_ROW_SCOPING_EL)) {
8063 wakaba 1.79
8064 wakaba 1.83 ## ISSUE: Can this state be reached?
8065 wakaba 1.54 pop @{$self->{open_elements}};
8066     }
8067    
8068     pop @{$self->{open_elements}}; # tr
8069 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
8070 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
8071     }
8072    
8073 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
8074 wakaba 1.54 ## have an element in table scope
8075     my $i;
8076     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8077     my $node = $self->{open_elements}->[$_];
8078 wakaba 1.121 if ($node->[1] & TABLE_ROW_GROUP_EL) {
8079 wakaba 1.79
8080 wakaba 1.54 $i = $_;
8081     last INSCOPE;
8082 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8083 wakaba 1.79
8084 wakaba 1.54 last INSCOPE;
8085     }
8086     } # INSCOPE
8087     unless (defined $i) {
8088 wakaba 1.79
8089 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8090     text => $token->{tag_name}, token => $token);
8091 wakaba 1.54 ## Ignore the token
8092 wakaba 1.122
8093 wakaba 1.54 $token = $self->_get_next_token;
8094 wakaba 1.123 next B;
8095 wakaba 1.54 }
8096    
8097     ## Clear back to table body context
8098 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8099     & TABLE_ROWS_SCOPING_EL)) {
8100 wakaba 1.79
8101 wakaba 1.54 pop @{$self->{open_elements}};
8102     }
8103    
8104     ## As if <{current node}>
8105     ## have an element in table scope
8106     ## true by definition
8107    
8108     ## Clear back to table body context
8109     ## nop by definition
8110    
8111     pop @{$self->{open_elements}};
8112 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8113 wakaba 1.54 ## reprocess in the "in table" insertion mode...
8114     }
8115 wakaba 1.52
8116 wakaba 1.94 ## NOTE: </table> in the "in table" insertion mode.
8117     ## When you edit the code fragment below, please ensure that
8118     ## the code for <table> in the "in table" insertion mode
8119     ## is synced with it.
8120    
8121 wakaba 1.54 ## have a table element in table scope
8122     my $i;
8123     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8124     my $node = $self->{open_elements}->[$_];
8125 wakaba 1.121 if ($node->[1] & TABLE_EL) {
8126 wakaba 1.79
8127 wakaba 1.54 $i = $_;
8128     last INSCOPE;
8129 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8130 wakaba 1.79
8131 wakaba 1.54 last INSCOPE;
8132     }
8133     } # INSCOPE
8134     unless (defined $i) {
8135 wakaba 1.79
8136 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8137     text => $token->{tag_name}, token => $token);
8138 wakaba 1.54 ## Ignore the token
8139 wakaba 1.122
8140 wakaba 1.54 $token = $self->_get_next_token;
8141 wakaba 1.123 next B;
8142 wakaba 1.51 }
8143 wakaba 1.54
8144     splice @{$self->{open_elements}}, $i;
8145 wakaba 1.95 pop @{$open_tables};
8146 wakaba 1.54
8147     $self->_reset_insertion_mode;
8148 wakaba 1.1
8149     $token = $self->_get_next_token;
8150 wakaba 1.123 next B;
8151 wakaba 1.54 } elsif ({
8152     tbody => 1, tfoot => 1, thead => 1,
8153     }->{$token->{tag_name}} and
8154 wakaba 1.58 $self->{insertion_mode} & ROW_IMS) {
8155 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
8156 wakaba 1.54 ## have an element in table scope
8157     my $i;
8158     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8159     my $node = $self->{open_elements}->[$_];
8160 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8161 wakaba 1.79
8162 wakaba 1.54 $i = $_;
8163     last INSCOPE;
8164 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8165 wakaba 1.79
8166 wakaba 1.54 last INSCOPE;
8167     }
8168     } # INSCOPE
8169     unless (defined $i) {
8170 wakaba 1.79
8171 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8172     text => $token->{tag_name}, token => $token);
8173 wakaba 1.54 ## Ignore the token
8174 wakaba 1.122
8175 wakaba 1.54 $token = $self->_get_next_token;
8176 wakaba 1.123 next B;
8177 wakaba 1.54 }
8178    
8179     ## As if </tr>
8180     ## have an element in table scope
8181     my $i;
8182     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8183     my $node = $self->{open_elements}->[$_];
8184 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
8185 wakaba 1.79
8186 wakaba 1.54 $i = $_;
8187     last INSCOPE;
8188 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8189 wakaba 1.79
8190 wakaba 1.54 last INSCOPE;
8191     }
8192     } # INSCOPE
8193     unless (defined $i) {
8194 wakaba 1.79
8195 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8196     text => 'tr', token => $token);
8197 wakaba 1.54 ## Ignore the token
8198 wakaba 1.122
8199 wakaba 1.54 $token = $self->_get_next_token;
8200 wakaba 1.123 next B;
8201 wakaba 1.54 }
8202    
8203     ## Clear back to table row context
8204 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8205     & TABLE_ROW_SCOPING_EL)) {
8206 wakaba 1.79
8207 wakaba 1.83 ## ISSUE: Can this case be reached?
8208 wakaba 1.54 pop @{$self->{open_elements}};
8209     }
8210    
8211     pop @{$self->{open_elements}}; # tr
8212 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
8213 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
8214 wakaba 1.34 }
8215    
8216 wakaba 1.54 ## have an element in table scope
8217     my $i;
8218     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8219     my $node = $self->{open_elements}->[$_];
8220 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8221 wakaba 1.79
8222 wakaba 1.54 $i = $_;
8223     last INSCOPE;
8224 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
8225 wakaba 1.79
8226 wakaba 1.54 last INSCOPE;
8227 wakaba 1.34 }
8228 wakaba 1.54 } # INSCOPE
8229     unless (defined $i) {
8230 wakaba 1.79
8231 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8232     text => $token->{tag_name}, token => $token);
8233 wakaba 1.54 ## Ignore the token
8234 wakaba 1.122
8235 wakaba 1.54 $token = $self->_get_next_token;
8236 wakaba 1.123 next B;
8237 wakaba 1.34 }
8238    
8239 wakaba 1.54 ## Clear back to table body context
8240 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
8241     & TABLE_ROWS_SCOPING_EL)) {
8242 wakaba 1.79
8243 wakaba 1.83 ## ISSUE: Can this case be reached?
8244 wakaba 1.51 pop @{$self->{open_elements}};
8245 wakaba 1.25 }
8246 wakaba 1.51
8247 wakaba 1.54 pop @{$self->{open_elements}};
8248 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8249 wakaba 1.122
8250 wakaba 1.54 $token = $self->_get_next_token;
8251 wakaba 1.123 next B;
8252 wakaba 1.54 } elsif ({
8253     body => 1, caption => 1, col => 1, colgroup => 1,
8254     html => 1, td => 1, th => 1,
8255 wakaba 1.56 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
8256     tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
8257 wakaba 1.54 }->{$token->{tag_name}}) {
8258 wakaba 1.122
8259 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8260     text => $token->{tag_name}, token => $token);
8261 wakaba 1.122 ## Ignore the token
8262    
8263     $token = $self->_get_next_token;
8264 wakaba 1.123 next B;
8265 wakaba 1.60 } else {
8266 wakaba 1.79
8267 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in table:/',
8268     text => $token->{tag_name}, token => $token);
8269 wakaba 1.54
8270 wakaba 1.60 $insert = $insert_to_foster;
8271     #
8272     }
8273 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8274 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
8275 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8276 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
8277 wakaba 1.103
8278 wakaba 1.104 #
8279 wakaba 1.103 } else {
8280    
8281 wakaba 1.104 #
8282 wakaba 1.103 }
8283    
8284     ## Stop parsing
8285     last B;
8286 wakaba 1.60 } else {
8287     die "$0: $token->{type}: Unknown token type";
8288     }
8289 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
8290 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8291 wakaba 1.184 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
8292 wakaba 1.54 $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
8293     unless (length $token->{data}) {
8294 wakaba 1.79
8295 wakaba 1.54 $token = $self->_get_next_token;
8296 wakaba 1.123 next B;
8297 wakaba 1.25 }
8298 wakaba 1.54 }
8299    
8300 wakaba 1.79
8301 wakaba 1.54 #
8302 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
8303 wakaba 1.54 if ($token->{tag_name} eq 'col') {
8304    
8305 wakaba 1.79
8306 wakaba 1.25 {
8307     my $el;
8308    
8309 wakaba 1.1 $el = $self->{document}->create_element_ns
8310 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8311 wakaba 1.1
8312 wakaba 1.25 for my $attr_name (keys %{ $token->{attributes}}) {
8313 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8314 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8315 wakaba 1.117 $attr->value ($attr_t->{value});
8316     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8317     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8318     $el->set_attribute_node_ns ($attr);
8319 wakaba 1.1 }
8320    
8321 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8322     if defined $token->{line};
8323     $el->set_user_data (manakai_source_column => $token->{column})
8324     if defined $token->{column};
8325    
8326 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($el);
8327 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8328 wakaba 1.25 }
8329    
8330 wakaba 1.54 pop @{$self->{open_elements}};
8331 wakaba 1.122 delete $self->{self_closing};
8332 wakaba 1.54 $token = $self->_get_next_token;
8333 wakaba 1.123 next B;
8334 wakaba 1.54 } else {
8335 wakaba 1.79
8336 wakaba 1.54 #
8337     }
8338 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
8339 wakaba 1.54 if ($token->{tag_name} eq 'colgroup') {
8340 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
8341 wakaba 1.79
8342 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8343     text => 'colgroup', token => $token);
8344 wakaba 1.25 ## Ignore the token
8345 wakaba 1.42 $token = $self->_get_next_token;
8346 wakaba 1.123 next B;
8347 wakaba 1.24 } else {
8348 wakaba 1.79
8349 wakaba 1.54 pop @{$self->{open_elements}}; # colgroup
8350 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8351 wakaba 1.54 $token = $self->_get_next_token;
8352 wakaba 1.123 next B;
8353 wakaba 1.25 }
8354 wakaba 1.54 } elsif ($token->{tag_name} eq 'col') {
8355 wakaba 1.79
8356 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8357     text => 'col', token => $token);
8358 wakaba 1.54 ## Ignore the token
8359     $token = $self->_get_next_token;
8360 wakaba 1.123 next B;
8361 wakaba 1.54 } else {
8362 wakaba 1.79
8363 wakaba 1.54 #
8364     }
8365 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8366 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
8367 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8368    
8369     ## Stop parsing.
8370     last B;
8371     } else {
8372     ## NOTE: As if </colgroup>.
8373    
8374     pop @{$self->{open_elements}}; # colgroup
8375     $self->{insertion_mode} = IN_TABLE_IM;
8376     ## Reprocess.
8377 wakaba 1.123 next B;
8378 wakaba 1.103 }
8379     } else {
8380     die "$0: $token->{type}: Unknown token type";
8381     }
8382 wakaba 1.51
8383 wakaba 1.54 ## As if </colgroup>
8384 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
8385 wakaba 1.79
8386 wakaba 1.103 ## TODO: Wrong error type?
8387 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8388     text => 'colgroup', token => $token);
8389 wakaba 1.54 ## Ignore the token
8390 wakaba 1.122
8391 wakaba 1.54 $token = $self->_get_next_token;
8392 wakaba 1.123 next B;
8393 wakaba 1.54 } else {
8394 wakaba 1.79
8395 wakaba 1.54 pop @{$self->{open_elements}}; # colgroup
8396 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
8397 wakaba 1.122
8398 wakaba 1.54 ## reprocess
8399 wakaba 1.123 next B;
8400 wakaba 1.54 }
8401 wakaba 1.101 } elsif ($self->{insertion_mode} & SELECT_IMS) {
8402 wakaba 1.60 if ($token->{type} == CHARACTER_TOKEN) {
8403 wakaba 1.79
8404 wakaba 1.60 $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
8405     $token = $self->_get_next_token;
8406 wakaba 1.123 next B;
8407 wakaba 1.60 } elsif ($token->{type} == START_TAG_TOKEN) {
8408 wakaba 1.121 if ($token->{tag_name} eq 'option') {
8409     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8410    
8411     ## As if </option>
8412     pop @{$self->{open_elements}};
8413     } else {
8414    
8415     }
8416 wakaba 1.51
8417 wakaba 1.121
8418 wakaba 1.1 {
8419     my $el;
8420    
8421     $el = $self->{document}->create_element_ns
8422 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8423 wakaba 1.1
8424     for my $attr_name (keys %{ $token->{attributes}}) {
8425 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8426 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8427 wakaba 1.117 $attr->value ($attr_t->{value});
8428     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8429     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8430     $el->set_attribute_node_ns ($attr);
8431 wakaba 1.1 }
8432    
8433 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8434     if defined $token->{line};
8435     $el->set_user_data (manakai_source_column => $token->{column})
8436     if defined $token->{column};
8437    
8438 wakaba 1.3 $self->{open_elements}->[-1]->[0]->append_child ($el);
8439 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8440 wakaba 1.1 }
8441    
8442 wakaba 1.122
8443 wakaba 1.121 $token = $self->_get_next_token;
8444 wakaba 1.123 next B;
8445 wakaba 1.121 } elsif ($token->{tag_name} eq 'optgroup') {
8446     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8447    
8448     ## As if </option>
8449     pop @{$self->{open_elements}};
8450     } else {
8451    
8452     }
8453 wakaba 1.54
8454 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
8455    
8456     ## As if </optgroup>
8457     pop @{$self->{open_elements}};
8458     } else {
8459    
8460     }
8461 wakaba 1.51
8462 wakaba 1.121
8463 wakaba 1.1 {
8464     my $el;
8465    
8466     $el = $self->{document}->create_element_ns
8467 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8468 wakaba 1.1
8469 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
8470 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8471 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8472 wakaba 1.117 $attr->value ($attr_t->{value});
8473     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8474     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8475     $el->set_attribute_node_ns ($attr);
8476 wakaba 1.54 }
8477    
8478 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8479     if defined $token->{line};
8480     $el->set_user_data (manakai_source_column => $token->{column})
8481     if defined $token->{column};
8482    
8483 wakaba 1.3 $self->{open_elements}->[-1]->[0]->append_child ($el);
8484 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8485 wakaba 1.1 }
8486    
8487 wakaba 1.122
8488 wakaba 1.121 $token = $self->_get_next_token;
8489 wakaba 1.123 next B;
8490 wakaba 1.143 } elsif ({
8491     select => 1, input => 1, textarea => 1,
8492     }->{$token->{tag_name}} or
8493 wakaba 1.101 ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
8494     {
8495     caption => 1, table => 1,
8496     tbody => 1, tfoot => 1, thead => 1,
8497     tr => 1, td => 1, th => 1,
8498     }->{$token->{tag_name}})) {
8499     ## TODO: The type below is not good - <select> is replaced by </select>
8500 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed', text => 'select',
8501     token => $token);
8502 wakaba 1.101 ## NOTE: As if the token were </select> (<select> case) or
8503     ## as if there were </select> (otherwise).
8504 wakaba 1.121 ## have an element in table scope
8505     my $i;
8506     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8507     my $node = $self->{open_elements}->[$_];
8508     if ($node->[1] & SELECT_EL) {
8509    
8510     $i = $_;
8511     last INSCOPE;
8512     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8513 wakaba 1.54
8514 wakaba 1.121 last INSCOPE;
8515     }
8516     } # INSCOPE
8517     unless (defined $i) {
8518    
8519 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8520     text => 'select', token => $token);
8521 wakaba 1.121 ## Ignore the token
8522 wakaba 1.122
8523 wakaba 1.121 $token = $self->_get_next_token;
8524 wakaba 1.123 next B;
8525 wakaba 1.121 }
8526 wakaba 1.79
8527 wakaba 1.121
8528     splice @{$self->{open_elements}}, $i;
8529 wakaba 1.54
8530 wakaba 1.121 $self->_reset_insertion_mode;
8531 wakaba 1.54
8532 wakaba 1.101 if ($token->{tag_name} eq 'select') {
8533    
8534     $token = $self->_get_next_token;
8535 wakaba 1.123 next B;
8536 wakaba 1.101 } else {
8537    
8538 wakaba 1.122
8539 wakaba 1.101 ## Reprocess the token.
8540 wakaba 1.123 next B;
8541 wakaba 1.101 }
8542 wakaba 1.60 } else {
8543 wakaba 1.79
8544 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in select',
8545     text => $token->{tag_name}, token => $token);
8546 wakaba 1.60 ## Ignore the token
8547 wakaba 1.122
8548 wakaba 1.60 $token = $self->_get_next_token;
8549 wakaba 1.123 next B;
8550 wakaba 1.60 }
8551     } elsif ($token->{type} == END_TAG_TOKEN) {
8552 wakaba 1.121 if ($token->{tag_name} eq 'optgroup') {
8553     if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
8554     $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
8555    
8556     ## As if </option>
8557     splice @{$self->{open_elements}}, -2;
8558     } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
8559    
8560     pop @{$self->{open_elements}};
8561     } else {
8562    
8563 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8564     text => $token->{tag_name}, token => $token);
8565 wakaba 1.121 ## Ignore the token
8566     }
8567 wakaba 1.122
8568 wakaba 1.121 $token = $self->_get_next_token;
8569 wakaba 1.123 next B;
8570 wakaba 1.121 } elsif ($token->{tag_name} eq 'option') {
8571     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
8572    
8573     pop @{$self->{open_elements}};
8574     } else {
8575    
8576 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8577     text => $token->{tag_name}, token => $token);
8578 wakaba 1.121 ## Ignore the token
8579     }
8580 wakaba 1.122
8581 wakaba 1.121 $token = $self->_get_next_token;
8582 wakaba 1.123 next B;
8583 wakaba 1.121 } elsif ($token->{tag_name} eq 'select') {
8584     ## have an element in table scope
8585     my $i;
8586     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8587     my $node = $self->{open_elements}->[$_];
8588     if ($node->[1] & SELECT_EL) {
8589    
8590     $i = $_;
8591     last INSCOPE;
8592     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8593 wakaba 1.54
8594 wakaba 1.121 last INSCOPE;
8595     }
8596     } # INSCOPE
8597     unless (defined $i) {
8598    
8599 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8600     text => $token->{tag_name}, token => $token);
8601 wakaba 1.121 ## Ignore the token
8602 wakaba 1.122
8603 wakaba 1.121 $token = $self->_get_next_token;
8604 wakaba 1.123 next B;
8605 wakaba 1.121 }
8606 wakaba 1.79
8607 wakaba 1.121
8608     splice @{$self->{open_elements}}, $i;
8609 wakaba 1.54
8610 wakaba 1.121 $self->_reset_insertion_mode;
8611 wakaba 1.54
8612 wakaba 1.122
8613 wakaba 1.121 $token = $self->_get_next_token;
8614 wakaba 1.123 next B;
8615 wakaba 1.101 } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
8616     {
8617     caption => 1, table => 1, tbody => 1,
8618     tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
8619     }->{$token->{tag_name}}) {
8620 wakaba 1.83 ## TODO: The following is wrong?
8621 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8622     text => $token->{tag_name}, token => $token);
8623 wakaba 1.121
8624     ## have an element in table scope
8625     my $i;
8626     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8627     my $node = $self->{open_elements}->[$_];
8628     if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8629    
8630     $i = $_;
8631     last INSCOPE;
8632     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8633    
8634     last INSCOPE;
8635     }
8636     } # INSCOPE
8637     unless (defined $i) {
8638    
8639     ## Ignore the token
8640 wakaba 1.122
8641 wakaba 1.121 $token = $self->_get_next_token;
8642 wakaba 1.123 next B;
8643 wakaba 1.121 }
8644 wakaba 1.54
8645 wakaba 1.121 ## As if </select>
8646     ## have an element in table scope
8647     undef $i;
8648     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8649     my $node = $self->{open_elements}->[$_];
8650     if ($node->[1] & SELECT_EL) {
8651 wakaba 1.54
8652 wakaba 1.121 $i = $_;
8653     last INSCOPE;
8654     } elsif ($node->[1] & TABLE_SCOPING_EL) {
8655 wakaba 1.83 ## ISSUE: Can this state be reached?
8656 wakaba 1.121
8657     last INSCOPE;
8658     }
8659     } # INSCOPE
8660     unless (defined $i) {
8661    
8662 wakaba 1.83 ## TODO: The following error type is correct?
8663 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8664     text => 'select', token => $token);
8665 wakaba 1.121 ## Ignore the </select> token
8666 wakaba 1.122
8667 wakaba 1.121 $token = $self->_get_next_token; ## TODO: ok?
8668 wakaba 1.123 next B;
8669 wakaba 1.121 }
8670 wakaba 1.54
8671 wakaba 1.121
8672     splice @{$self->{open_elements}}, $i;
8673 wakaba 1.54
8674 wakaba 1.121 $self->_reset_insertion_mode;
8675 wakaba 1.54
8676 wakaba 1.122
8677 wakaba 1.121 ## reprocess
8678 wakaba 1.123 next B;
8679 wakaba 1.60 } else {
8680 wakaba 1.79
8681 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in select:/',
8682     text => $token->{tag_name}, token => $token);
8683 wakaba 1.54 ## Ignore the token
8684 wakaba 1.122
8685 wakaba 1.54 $token = $self->_get_next_token;
8686 wakaba 1.123 next B;
8687 wakaba 1.60 }
8688 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8689 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
8690 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8691    
8692 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
8693 wakaba 1.103 } else {
8694    
8695     }
8696    
8697     ## Stop parsing.
8698     last B;
8699 wakaba 1.60 } else {
8700     die "$0: $token->{type}: Unknown token type";
8701     }
8702 wakaba 1.58 } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
8703 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8704 wakaba 1.184 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
8705 wakaba 1.54 my $data = $1;
8706     ## As if in body
8707     $reconstruct_active_formatting_elements->($insert_to_current);
8708    
8709     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
8710    
8711     unless (length $token->{data}) {
8712 wakaba 1.79
8713 wakaba 1.54 $token = $self->_get_next_token;
8714 wakaba 1.123 next B;
8715 wakaba 1.54 }
8716     }
8717    
8718 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
8719 wakaba 1.79
8720 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:#text', token => $token);
8721 wakaba 1.184 #
8722 wakaba 1.79 } else {
8723    
8724 wakaba 1.184 ## "after body" insertion mode
8725     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body:#text', token => $token);
8726     #
8727 wakaba 1.54 }
8728    
8729 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
8730 wakaba 1.54 ## reprocess
8731 wakaba 1.123 next B;
8732 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
8733 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
8734 wakaba 1.79
8735 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html',
8736     text => $token->{tag_name}, token => $token);
8737 wakaba 1.184 #
8738 wakaba 1.79 } else {
8739    
8740 wakaba 1.184 ## "after body" insertion mode
8741     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body',
8742     text => $token->{tag_name}, token => $token);
8743     #
8744 wakaba 1.54 }
8745    
8746 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
8747 wakaba 1.122
8748 wakaba 1.54 ## reprocess
8749 wakaba 1.123 next B;
8750 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
8751 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
8752 wakaba 1.79
8753 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:/',
8754     text => $token->{tag_name}, token => $token);
8755 wakaba 1.54
8756 wakaba 1.184 $self->{insertion_mode} = IN_BODY_IM;
8757     ## Reprocess.
8758     next B;
8759 wakaba 1.79 } else {
8760    
8761 wakaba 1.54 }
8762    
8763     ## "after body" insertion mode
8764     if ($token->{tag_name} eq 'html') {
8765     if (defined $self->{inner_html_node}) {
8766 wakaba 1.79
8767 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
8768     text => 'html', token => $token);
8769 wakaba 1.54 ## Ignore the token
8770     $token = $self->_get_next_token;
8771 wakaba 1.123 next B;
8772 wakaba 1.54 } else {
8773 wakaba 1.79
8774 wakaba 1.56 $self->{insertion_mode} = AFTER_HTML_BODY_IM;
8775 wakaba 1.54 $token = $self->_get_next_token;
8776 wakaba 1.123 next B;
8777 wakaba 1.54 }
8778     } else {
8779 wakaba 1.79
8780 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after body:/',
8781     text => $token->{tag_name}, token => $token);
8782 wakaba 1.54
8783 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
8784 wakaba 1.54 ## reprocess
8785 wakaba 1.123 next B;
8786 wakaba 1.54 }
8787 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8788    
8789     ## Stop parsing
8790     last B;
8791 wakaba 1.54 } else {
8792     die "$0: $token->{type}: Unknown token type";
8793     }
8794 wakaba 1.58 } elsif ($self->{insertion_mode} & FRAME_IMS) {
8795 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8796 wakaba 1.184 if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
8797 wakaba 1.54 $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
8798    
8799     unless (length $token->{data}) {
8800 wakaba 1.79
8801 wakaba 1.54 $token = $self->_get_next_token;
8802 wakaba 1.123 next B;
8803 wakaba 1.54 }
8804     }
8805    
8806 wakaba 1.184 if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
8807 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
8808 wakaba 1.79
8809 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset:#text', token => $token);
8810 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
8811 wakaba 1.79
8812 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset:#text', token => $token);
8813 wakaba 1.154 } else { # "after after frameset"
8814 wakaba 1.79
8815 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after html:#text', token => $token);
8816 wakaba 1.54 }
8817    
8818     ## Ignore the token.
8819     if (length $token->{data}) {
8820 wakaba 1.79
8821 wakaba 1.54 ## reprocess the rest of characters
8822     } else {
8823 wakaba 1.79
8824 wakaba 1.54 $token = $self->_get_next_token;
8825     }
8826 wakaba 1.123 next B;
8827 wakaba 1.54 }
8828    
8829     die qq[$0: Character "$token->{data}"];
8830 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
8831 wakaba 1.54 if ($token->{tag_name} eq 'frameset' and
8832 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
8833 wakaba 1.54
8834 wakaba 1.79
8835 wakaba 1.54 {
8836     my $el;
8837    
8838     $el = $self->{document}->create_element_ns
8839 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8840 wakaba 1.54
8841     for my $attr_name (keys %{ $token->{attributes}}) {
8842 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8843 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8844 wakaba 1.117 $attr->value ($attr_t->{value});
8845     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8846     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8847     $el->set_attribute_node_ns ($attr);
8848 wakaba 1.54 }
8849    
8850 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8851     if defined $token->{line};
8852     $el->set_user_data (manakai_source_column => $token->{column})
8853     if defined $token->{column};
8854    
8855 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
8856 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8857 wakaba 1.54 }
8858    
8859 wakaba 1.122
8860 wakaba 1.54 $token = $self->_get_next_token;
8861 wakaba 1.123 next B;
8862 wakaba 1.54 } elsif ($token->{tag_name} eq 'frame' and
8863 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
8864 wakaba 1.54
8865 wakaba 1.79
8866 wakaba 1.54 {
8867     my $el;
8868    
8869     $el = $self->{document}->create_element_ns
8870 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8871 wakaba 1.54
8872     for my $attr_name (keys %{ $token->{attributes}}) {
8873 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8874 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8875 wakaba 1.117 $attr->value ($attr_t->{value});
8876     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8877     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8878     $el->set_attribute_node_ns ($attr);
8879 wakaba 1.54 }
8880    
8881 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8882     if defined $token->{line};
8883     $el->set_user_data (manakai_source_column => $token->{column})
8884     if defined $token->{column};
8885    
8886 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
8887 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8888 wakaba 1.54 }
8889    
8890     pop @{$self->{open_elements}};
8891 wakaba 1.122 delete $self->{self_closing};
8892 wakaba 1.54 $token = $self->_get_next_token;
8893 wakaba 1.123 next B;
8894 wakaba 1.54 } elsif ($token->{tag_name} eq 'noframes') {
8895 wakaba 1.79
8896 wakaba 1.145 ## NOTE: As if in head.
8897 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
8898 wakaba 1.123 next B;
8899 wakaba 1.155
8900     ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
8901     ## has no parse error.
8902 wakaba 1.54 } else {
8903 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
8904 wakaba 1.79
8905 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset',
8906     text => $token->{tag_name}, token => $token);
8907 wakaba 1.154 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
8908 wakaba 1.79
8909 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset',
8910     text => $token->{tag_name}, token => $token);
8911 wakaba 1.154 } else { # "after after frameset"
8912    
8913     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after after frameset',
8914     text => $token->{tag_name}, token => $token);
8915 wakaba 1.54 }
8916     ## Ignore the token
8917 wakaba 1.122
8918 wakaba 1.54 $token = $self->_get_next_token;
8919 wakaba 1.123 next B;
8920 wakaba 1.54 }
8921 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
8922 wakaba 1.54 if ($token->{tag_name} eq 'frameset' and
8923 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
8924 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
8925 wakaba 1.54 @{$self->{open_elements}} == 1) {
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     } else {
8932 wakaba 1.79
8933 wakaba 1.54 pop @{$self->{open_elements}};
8934     $token = $self->_get_next_token;
8935     }
8936 wakaba 1.43
8937 wakaba 1.54 if (not defined $self->{inner_html_node} and
8938 wakaba 1.121 not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
8939 wakaba 1.79
8940 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
8941 wakaba 1.79 } else {
8942    
8943 wakaba 1.54 }
8944 wakaba 1.123 next B;
8945 wakaba 1.54 } elsif ($token->{tag_name} eq 'html' and
8946 wakaba 1.56 $self->{insertion_mode} == AFTER_FRAMESET_IM) {
8947 wakaba 1.79
8948 wakaba 1.56 $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
8949 wakaba 1.54 $token = $self->_get_next_token;
8950 wakaba 1.123 next B;
8951 wakaba 1.54 } else {
8952 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
8953 wakaba 1.79
8954 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in frameset:/',
8955     text => $token->{tag_name}, token => $token);
8956 wakaba 1.154 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
8957 wakaba 1.79
8958 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'after frameset:/',
8959     text => $token->{tag_name}, token => $token);
8960 wakaba 1.154 } else { # "after after html"
8961    
8962     $self->{parse_error}->(level => $self->{level}->{must}, type => 'after after frameset:/',
8963     text => $token->{tag_name}, token => $token);
8964 wakaba 1.54 }
8965     ## Ignore the token
8966     $token = $self->_get_next_token;
8967 wakaba 1.123 next B;
8968 wakaba 1.54 }
8969 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
8970 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
8971 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
8972    
8973 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body:#eof', token => $token);
8974 wakaba 1.103 } else {
8975    
8976     }
8977    
8978     ## Stop parsing
8979     last B;
8980 wakaba 1.54 } else {
8981     die "$0: $token->{type}: Unknown token type";
8982     }
8983     } else {
8984     die "$0: $self->{insertion_mode}: Unknown insertion mode";
8985     }
8986 wakaba 1.43
8987 wakaba 1.54 ## "in body" insertion mode
8988 wakaba 1.57 if ($token->{type} == START_TAG_TOKEN) {
8989 wakaba 1.54 if ($token->{tag_name} eq 'script') {
8990 wakaba 1.79
8991 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
8992 wakaba 1.100 $script_start_tag->();
8993 wakaba 1.123 next B;
8994 wakaba 1.54 } elsif ($token->{tag_name} eq 'style') {
8995 wakaba 1.79
8996 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
8997 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
8998 wakaba 1.123 next B;
8999 wakaba 1.54 } elsif ({
9000 wakaba 1.189 base => 1, command => 1, eventsource => 1, link => 1,
9001 wakaba 1.54 }->{$token->{tag_name}}) {
9002 wakaba 1.79
9003 wakaba 1.54 ## NOTE: This is an "as if in head" code clone, only "-t" differs
9004    
9005     {
9006     my $el;
9007    
9008     $el = $self->{document}->create_element_ns
9009 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9010 wakaba 1.54
9011     for my $attr_name (keys %{ $token->{attributes}}) {
9012 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9013 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9014 wakaba 1.117 $attr->value ($attr_t->{value});
9015     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9016     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9017     $el->set_attribute_node_ns ($attr);
9018 wakaba 1.54 }
9019    
9020 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9021     if defined $token->{line};
9022     $el->set_user_data (manakai_source_column => $token->{column})
9023     if defined $token->{column};
9024    
9025 wakaba 1.54 $insert->($el);
9026 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9027 wakaba 1.54 }
9028    
9029 wakaba 1.189 pop @{$self->{open_elements}};
9030 wakaba 1.122 delete $self->{self_closing};
9031 wakaba 1.54 $token = $self->_get_next_token;
9032 wakaba 1.123 next B;
9033 wakaba 1.54 } elsif ($token->{tag_name} eq 'meta') {
9034     ## NOTE: This is an "as if in head" code clone, only "-t" differs
9035    
9036     {
9037     my $el;
9038    
9039     $el = $self->{document}->create_element_ns
9040 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9041 wakaba 1.54
9042     for my $attr_name (keys %{ $token->{attributes}}) {
9043 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9044 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9045 wakaba 1.117 $attr->value ($attr_t->{value});
9046     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9047     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9048     $el->set_attribute_node_ns ($attr);
9049 wakaba 1.54 }
9050    
9051 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9052     if defined $token->{line};
9053     $el->set_user_data (manakai_source_column => $token->{column})
9054     if defined $token->{column};
9055    
9056 wakaba 1.54 $insert->($el);
9057 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9058 wakaba 1.54 }
9059    
9060 wakaba 1.189 my $meta_el = pop @{$self->{open_elements}};
9061 wakaba 1.43
9062 wakaba 1.54 unless ($self->{confident}) {
9063 wakaba 1.131 if ($token->{attributes}->{charset}) {
9064 wakaba 1.79
9065 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
9066     ## in the {change_encoding} callback.
9067 wakaba 1.65 $self->{change_encoding}
9068 wakaba 1.112 ->($self, $token->{attributes}->{charset}->{value}, $token);
9069 wakaba 1.68
9070     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
9071     ->set_user_data (manakai_has_reference =>
9072     $token->{attributes}->{charset}
9073     ->{has_reference});
9074 wakaba 1.65 } elsif ($token->{attributes}->{content}) {
9075     if ($token->{attributes}->{content}->{value}
9076 wakaba 1.141 =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
9077 wakaba 1.185 [\x09\x0A\x0C\x0D\x20]*=
9078     [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
9079     ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
9080     /x) {
9081 wakaba 1.79
9082 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
9083     ## in the {change_encoding} callback.
9084 wakaba 1.65 $self->{change_encoding}
9085 wakaba 1.112 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
9086 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
9087     ->set_user_data (manakai_has_reference =>
9088     $token->{attributes}->{content}
9089     ->{has_reference});
9090 wakaba 1.65 }
9091 wakaba 1.54 }
9092 wakaba 1.68 } else {
9093     if ($token->{attributes}->{charset}) {
9094 wakaba 1.79
9095 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
9096     ->set_user_data (manakai_has_reference =>
9097     $token->{attributes}->{charset}
9098     ->{has_reference});
9099     }
9100 wakaba 1.69 if ($token->{attributes}->{content}) {
9101 wakaba 1.79
9102 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
9103     ->set_user_data (manakai_has_reference =>
9104     $token->{attributes}->{content}
9105     ->{has_reference});
9106     }
9107 wakaba 1.54 }
9108 wakaba 1.43
9109 wakaba 1.122 delete $self->{self_closing};
9110 wakaba 1.54 $token = $self->_get_next_token;
9111 wakaba 1.123 next B;
9112 wakaba 1.54 } elsif ($token->{tag_name} eq 'title') {
9113 wakaba 1.79
9114 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
9115 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
9116 wakaba 1.123 next B;
9117 wakaba 1.54 } elsif ($token->{tag_name} eq 'body') {
9118 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body', text => 'body', token => $token);
9119 wakaba 1.1
9120 wakaba 1.54 if (@{$self->{open_elements}} == 1 or
9121 wakaba 1.121 not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
9122 wakaba 1.79
9123 wakaba 1.54 ## Ignore the token
9124     } else {
9125     my $body_el = $self->{open_elements}->[1]->[0];
9126     for my $attr_name (keys %{$token->{attributes}}) {
9127     unless ($body_el->has_attribute_ns (undef, $attr_name)) {
9128 wakaba 1.79
9129 wakaba 1.54 $body_el->set_attribute_ns
9130     (undef, [undef, $attr_name],
9131     $token->{attributes}->{$attr_name}->{value});
9132 wakaba 1.1 }
9133 wakaba 1.54 }
9134     }
9135 wakaba 1.122
9136 wakaba 1.54 $token = $self->_get_next_token;
9137 wakaba 1.123 next B;
9138 wakaba 1.54 } elsif ({
9139 wakaba 1.190 ## NOTE: Start tags for non-phrasing flow content elements
9140    
9141     ## NOTE: The normal one
9142     address => 1, article => 1, aside => 1, blockquote => 1,
9143     center => 1, datagrid => 1, details => 1, dialog => 1,
9144     dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
9145     footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
9146     h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
9147     section => 1, ul => 1,
9148     ## NOTE: As normal, but drops leading newline
9149 wakaba 1.97 pre => 1, listing => 1,
9150 wakaba 1.190 ## NOTE: As normal, but interacts with the form element pointer
9151 wakaba 1.107 form => 1,
9152 wakaba 1.190
9153 wakaba 1.107 table => 1,
9154     hr => 1,
9155 wakaba 1.54 }->{$token->{tag_name}}) {
9156 wakaba 1.107 if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
9157    
9158 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in form:form', token => $token);
9159 wakaba 1.107 ## Ignore the token
9160 wakaba 1.122
9161 wakaba 1.107 $token = $self->_get_next_token;
9162 wakaba 1.123 next B;
9163 wakaba 1.107 }
9164    
9165 wakaba 1.54 ## has a p element in scope
9166     INSCOPE: for (reverse @{$self->{open_elements}}) {
9167 wakaba 1.121 if ($_->[1] & P_EL) {
9168 wakaba 1.79
9169 wakaba 1.122
9170     $token->{self_closing} = $self->{self_closing};
9171     unshift @{$self->{token}}, $token;
9172     delete $self->{self_closing};
9173     # <form>
9174 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9175     line => $token->{line}, column => $token->{column}};
9176 wakaba 1.123 next B;
9177 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9178 wakaba 1.79
9179 wakaba 1.54 last INSCOPE;
9180     }
9181     } # INSCOPE
9182    
9183    
9184 wakaba 1.48 {
9185     my $el;
9186    
9187     $el = $self->{document}->create_element_ns
9188 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9189 wakaba 1.48
9190 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
9191 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9192 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9193 wakaba 1.117 $attr->value ($attr_t->{value});
9194     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9195     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9196     $el->set_attribute_node_ns ($attr);
9197 wakaba 1.54 }
9198    
9199 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9200     if defined $token->{line};
9201     $el->set_user_data (manakai_source_column => $token->{column})
9202     if defined $token->{column};
9203    
9204 wakaba 1.54 $insert->($el);
9205 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9206 wakaba 1.48 }
9207    
9208 wakaba 1.97 if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
9209 wakaba 1.122
9210 wakaba 1.54 $token = $self->_get_next_token;
9211 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
9212 wakaba 1.54 $token->{data} =~ s/^\x0A//;
9213     unless (length $token->{data}) {
9214 wakaba 1.79
9215 wakaba 1.54 $token = $self->_get_next_token;
9216 wakaba 1.79 } else {
9217    
9218 wakaba 1.54 }
9219 wakaba 1.79 } else {
9220    
9221 wakaba 1.54 }
9222 wakaba 1.107 } elsif ($token->{tag_name} eq 'form') {
9223    
9224     $self->{form_element} = $self->{open_elements}->[-1]->[0];
9225    
9226 wakaba 1.122
9227 wakaba 1.107 $token = $self->_get_next_token;
9228     } elsif ($token->{tag_name} eq 'table') {
9229    
9230     push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
9231 wakaba 1.79
9232 wakaba 1.107 $self->{insertion_mode} = IN_TABLE_IM;
9233    
9234 wakaba 1.122
9235 wakaba 1.54 $token = $self->_get_next_token;
9236 wakaba 1.107 } elsif ($token->{tag_name} eq 'hr') {
9237 wakaba 1.79
9238 wakaba 1.107 pop @{$self->{open_elements}};
9239    
9240 wakaba 1.122
9241 wakaba 1.54 $token = $self->_get_next_token;
9242     } else {
9243    
9244     $token = $self->_get_next_token;
9245     }
9246 wakaba 1.123 next B;
9247 wakaba 1.191 } elsif ($token->{tag_name} eq 'li') {
9248     ## NOTE: As normal, but imply </li> when there's another <li> ...
9249    
9250     ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
9251     ## Interpreted as <li><foo/></li><li/> (non-conforming)
9252     ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
9253     ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
9254     ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
9255     ## object (Fx)
9256     ## Generate non-tree (non-conforming)
9257     ## basefont (IE7 (where basefont is non-void)), center (IE),
9258     ## form (IE), hn (IE)
9259     ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
9260     ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
9261     ## div (Fx, S)
9262    
9263     my $non_optional;
9264     my $i = -1;
9265    
9266     ## 1.
9267     for my $node (reverse @{$self->{open_elements}}) {
9268     if ($node->[1] & LI_EL) {
9269     ## 2. (a) As if </li>
9270     {
9271     ## If no </li> - not applied
9272     #
9273    
9274     ## Otherwise
9275    
9276     ## 1. generate implied end tags, except for </li>
9277     #
9278    
9279     ## 2. If current node != "li", parse error
9280     if ($non_optional) {
9281     $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9282     text => $non_optional->[0]->manakai_local_name,
9283     token => $token);
9284    
9285     } else {
9286    
9287     }
9288    
9289     ## 3. Pop
9290     splice @{$self->{open_elements}}, $i;
9291     }
9292    
9293     last; ## 2. (b) goto 5.
9294     } elsif (
9295     ## NOTE: not "formatting" and not "phrasing"
9296     ($node->[1] & SPECIAL_EL or
9297     $node->[1] & SCOPING_EL) and
9298     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
9299    
9300     (not $node->[1] & ADDRESS_EL) &
9301     (not $node->[1] & DIV_EL) &
9302     (not $node->[1] & P_EL)) {
9303     ## 3.
9304    
9305     last; ## goto 5.
9306     } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
9307    
9308     #
9309     } else {
9310    
9311     $non_optional ||= $node;
9312     #
9313     }
9314     ## 4.
9315     ## goto 2.
9316     $i--;
9317     }
9318    
9319     ## 5. (a) has a |p| element in scope
9320 wakaba 1.54 INSCOPE: for (reverse @{$self->{open_elements}}) {
9321 wakaba 1.121 if ($_->[1] & P_EL) {
9322 wakaba 1.79
9323 wakaba 1.193
9324     ## NOTE: |<p><li>|, for example.
9325    
9326 wakaba 1.122
9327     $token->{self_closing} = $self->{self_closing};
9328     unshift @{$self->{token}}, $token;
9329     delete $self->{self_closing};
9330     # <x>
9331 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9332     line => $token->{line}, column => $token->{column}};
9333 wakaba 1.123 next B;
9334 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9335 wakaba 1.79
9336 wakaba 1.54 last INSCOPE;
9337     }
9338     } # INSCOPE
9339 wakaba 1.188
9340 wakaba 1.191 ## 5. (b) insert
9341    
9342     {
9343     my $el;
9344    
9345     $el = $self->{document}->create_element_ns
9346     ($HTML_NS, [undef, $token->{tag_name}]);
9347    
9348     for my $attr_name (keys %{ $token->{attributes}}) {
9349     my $attr_t = $token->{attributes}->{$attr_name};
9350     my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9351     $attr->value ($attr_t->{value});
9352     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9353     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9354     $el->set_attribute_node_ns ($attr);
9355     }
9356    
9357     $el->set_user_data (manakai_source_line => $token->{line})
9358     if defined $token->{line};
9359     $el->set_user_data (manakai_source_column => $token->{column})
9360     if defined $token->{column};
9361    
9362     $insert->($el);
9363     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9364     }
9365    
9366    
9367     $token = $self->_get_next_token;
9368     next B;
9369     } elsif ($token->{tag_name} eq 'dt' or
9370     $token->{tag_name} eq 'dd') {
9371     ## NOTE: As normal, but imply </dt> or </dd> when ...
9372    
9373     my $non_optional;
9374 wakaba 1.54 my $i = -1;
9375 wakaba 1.191
9376     ## 1.
9377     for my $node (reverse @{$self->{open_elements}}) {
9378     if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
9379     ## 2. (a) As if </li>
9380     {
9381     ## If no </li> - not applied
9382     #
9383    
9384     ## Otherwise
9385    
9386     ## 1. generate implied end tags, except for </dt> or </dd>
9387     #
9388    
9389     ## 2. If current node != "dt"|"dd", parse error
9390     if ($non_optional) {
9391     $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9392     text => $non_optional->[0]->manakai_local_name,
9393     token => $token);
9394    
9395     } else {
9396    
9397     }
9398    
9399     ## 3. Pop
9400     splice @{$self->{open_elements}}, $i;
9401 wakaba 1.54 }
9402 wakaba 1.191
9403     last; ## 2. (b) goto 5.
9404     } elsif (
9405     ## NOTE: not "formatting" and not "phrasing"
9406     ($node->[1] & SPECIAL_EL or
9407     $node->[1] & SCOPING_EL) and
9408     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
9409    
9410     (not $node->[1] & ADDRESS_EL) &
9411     (not $node->[1] & DIV_EL) &
9412     (not $node->[1] & P_EL)) {
9413     ## 3.
9414    
9415     last; ## goto 5.
9416     } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
9417    
9418     #
9419 wakaba 1.79 } else {
9420    
9421 wakaba 1.191 $non_optional ||= $node;
9422     #
9423 wakaba 1.54 }
9424 wakaba 1.191 ## 4.
9425     ## goto 2.
9426     $i--;
9427     }
9428    
9429     ## 5. (a) has a |p| element in scope
9430     INSCOPE: for (reverse @{$self->{open_elements}}) {
9431     if ($_->[1] & P_EL) {
9432    
9433    
9434     $token->{self_closing} = $self->{self_closing};
9435     unshift @{$self->{token}}, $token;
9436     delete $self->{self_closing};
9437     # <x>
9438     $token = {type => END_TAG_TOKEN, tag_name => 'p',
9439     line => $token->{line}, column => $token->{column}};
9440     next B;
9441     } elsif ($_->[1] & SCOPING_EL) {
9442 wakaba 1.79
9443 wakaba 1.191 last INSCOPE;
9444 wakaba 1.54 }
9445 wakaba 1.191 } # INSCOPE
9446    
9447     ## 5. (b) insert
9448 wakaba 1.54
9449 wakaba 1.49 {
9450     my $el;
9451    
9452     $el = $self->{document}->create_element_ns
9453 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9454 wakaba 1.49
9455     for my $attr_name (keys %{ $token->{attributes}}) {
9456 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9457 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9458 wakaba 1.117 $attr->value ($attr_t->{value});
9459     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9460     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9461     $el->set_attribute_node_ns ($attr);
9462 wakaba 1.49 }
9463    
9464 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9465     if defined $token->{line};
9466     $el->set_user_data (manakai_source_column => $token->{column})
9467     if defined $token->{column};
9468    
9469 wakaba 1.54 $insert->($el);
9470 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9471 wakaba 1.49 }
9472    
9473 wakaba 1.122
9474 wakaba 1.54 $token = $self->_get_next_token;
9475 wakaba 1.123 next B;
9476 wakaba 1.54 } elsif ($token->{tag_name} eq 'plaintext') {
9477 wakaba 1.190 ## NOTE: As normal, but effectively ends parsing
9478    
9479 wakaba 1.54 ## has a p element in scope
9480     INSCOPE: for (reverse @{$self->{open_elements}}) {
9481 wakaba 1.121 if ($_->[1] & P_EL) {
9482 wakaba 1.79
9483 wakaba 1.122
9484     $token->{self_closing} = $self->{self_closing};
9485     unshift @{$self->{token}}, $token;
9486     delete $self->{self_closing};
9487     # <plaintext>
9488 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
9489     line => $token->{line}, column => $token->{column}};
9490 wakaba 1.123 next B;
9491 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
9492 wakaba 1.79
9493 wakaba 1.54 last INSCOPE;
9494     }
9495     } # INSCOPE
9496    
9497    
9498 wakaba 1.1 {
9499     my $el;
9500    
9501     $el = $self->{document}->create_element_ns
9502 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9503 wakaba 1.1
9504 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
9505 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9506 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9507 wakaba 1.117 $attr->value ($attr_t->{value});
9508     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9509     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9510     $el->set_attribute_node_ns ($attr);
9511 wakaba 1.54 }
9512    
9513 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9514     if defined $token->{line};
9515     $el->set_user_data (manakai_source_column => $token->{column})
9516     if defined $token->{column};
9517    
9518 wakaba 1.54 $insert->($el);
9519 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9520 wakaba 1.1 }
9521    
9522 wakaba 1.54
9523     $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
9524    
9525 wakaba 1.122
9526 wakaba 1.54 $token = $self->_get_next_token;
9527 wakaba 1.123 next B;
9528 wakaba 1.54 } elsif ($token->{tag_name} eq 'a') {
9529     AFE: for my $i (reverse 0..$#$active_formatting_elements) {
9530     my $node = $active_formatting_elements->[$i];
9531 wakaba 1.121 if ($node->[1] & A_EL) {
9532 wakaba 1.79
9533 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in a:a', token => $token);
9534 wakaba 1.54
9535 wakaba 1.122
9536     $token->{self_closing} = $self->{self_closing};
9537     unshift @{$self->{token}}, $token;
9538     delete $self->{self_closing};
9539     # <a>
9540 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'a',
9541     line => $token->{line}, column => $token->{column}};
9542 wakaba 1.111 $formatting_end_tag->($token);
9543 wakaba 1.54
9544     AFE2: for (reverse 0..$#$active_formatting_elements) {
9545     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
9546 wakaba 1.79
9547 wakaba 1.54 splice @$active_formatting_elements, $_, 1;
9548     last AFE2;
9549 wakaba 1.49 }
9550 wakaba 1.54 } # AFE2
9551     OE: for (reverse 0..$#{$self->{open_elements}}) {
9552     if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
9553 wakaba 1.79
9554 wakaba 1.54 splice @{$self->{open_elements}}, $_, 1;
9555     last OE;
9556 wakaba 1.49 }
9557 wakaba 1.54 } # OE
9558     last AFE;
9559     } elsif ($node->[0] eq '#marker') {
9560 wakaba 1.79
9561 wakaba 1.54 last AFE;
9562     }
9563     } # AFE
9564    
9565     $reconstruct_active_formatting_elements->($insert_to_current);
9566 wakaba 1.49
9567 wakaba 1.54
9568     {
9569     my $el;
9570    
9571     $el = $self->{document}->create_element_ns
9572 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9573 wakaba 1.54
9574     for my $attr_name (keys %{ $token->{attributes}}) {
9575 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9576 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9577 wakaba 1.117 $attr->value ($attr_t->{value});
9578     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9579     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9580     $el->set_attribute_node_ns ($attr);
9581 wakaba 1.54 }
9582    
9583 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9584     if defined $token->{line};
9585     $el->set_user_data (manakai_source_column => $token->{column})
9586     if defined $token->{column};
9587    
9588 wakaba 1.54 $insert->($el);
9589 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9590 wakaba 1.54 }
9591    
9592     push @$active_formatting_elements, $self->{open_elements}->[-1];
9593 wakaba 1.48
9594 wakaba 1.122
9595 wakaba 1.54 $token = $self->_get_next_token;
9596 wakaba 1.123 next B;
9597 wakaba 1.54 } elsif ($token->{tag_name} eq 'nobr') {
9598     $reconstruct_active_formatting_elements->($insert_to_current);
9599 wakaba 1.1
9600 wakaba 1.54 ## has a |nobr| element in scope
9601     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9602     my $node = $self->{open_elements}->[$_];
9603 wakaba 1.121 if ($node->[1] & NOBR_EL) {
9604 wakaba 1.79
9605 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in nobr:nobr', token => $token);
9606 wakaba 1.122
9607     $token->{self_closing} = $self->{self_closing};
9608     unshift @{$self->{token}}, $token;
9609     delete $self->{self_closing};
9610     # <nobr>
9611 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
9612     line => $token->{line}, column => $token->{column}};
9613 wakaba 1.123 next B;
9614 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9615 wakaba 1.79
9616 wakaba 1.54 last INSCOPE;
9617     }
9618     } # INSCOPE
9619    
9620    
9621     {
9622     my $el;
9623    
9624     $el = $self->{document}->create_element_ns
9625 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9626 wakaba 1.54
9627     for my $attr_name (keys %{ $token->{attributes}}) {
9628 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9629 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9630 wakaba 1.117 $attr->value ($attr_t->{value});
9631     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9632     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9633     $el->set_attribute_node_ns ($attr);
9634 wakaba 1.54 }
9635    
9636 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9637     if defined $token->{line};
9638     $el->set_user_data (manakai_source_column => $token->{column})
9639     if defined $token->{column};
9640    
9641 wakaba 1.54 $insert->($el);
9642 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9643 wakaba 1.54 }
9644    
9645     push @$active_formatting_elements, $self->{open_elements}->[-1];
9646    
9647 wakaba 1.122
9648 wakaba 1.54 $token = $self->_get_next_token;
9649 wakaba 1.123 next B;
9650 wakaba 1.54 } elsif ($token->{tag_name} eq 'button') {
9651     ## has a button element in scope
9652     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9653     my $node = $self->{open_elements}->[$_];
9654 wakaba 1.121 if ($node->[1] & BUTTON_EL) {
9655 wakaba 1.79
9656 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in button:button', token => $token);
9657 wakaba 1.122
9658     $token->{self_closing} = $self->{self_closing};
9659     unshift @{$self->{token}}, $token;
9660     delete $self->{self_closing};
9661     # <button>
9662 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'button',
9663     line => $token->{line}, column => $token->{column}};
9664 wakaba 1.123 next B;
9665 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
9666 wakaba 1.79
9667 wakaba 1.54 last INSCOPE;
9668 wakaba 1.1 }
9669 wakaba 1.54 } # INSCOPE
9670    
9671     $reconstruct_active_formatting_elements->($insert_to_current);
9672    
9673    
9674     {
9675     my $el;
9676    
9677     $el = $self->{document}->create_element_ns
9678 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9679 wakaba 1.54
9680     for my $attr_name (keys %{ $token->{attributes}}) {
9681 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
9682 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9683 wakaba 1.117 $attr->value ($attr_t->{value});
9684     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9685     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9686     $el->set_attribute_node_ns ($attr);
9687 wakaba 1.54 }
9688    
9689 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9690     if defined $token->{line};
9691     $el->set_user_data (manakai_source_column => $token->{column})
9692     if defined $token->{column};
9693    
9694 wakaba 1.54 $insert->($el);
9695 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9696 wakaba 1.54 }
9697    
9698 wakaba 1.86
9699     ## TODO: associate with $self->{form_element} if defined
9700    
9701 wakaba 1.54 push @$active_formatting_elements, ['#marker', ''];
9702 wakaba 1.1
9703 wakaba 1.122
9704 wakaba 1.54 $token = $self->_get_next_token;
9705 wakaba 1.123 next B;
9706 wakaba 1.102 } elsif ({
9707 wakaba 1.107 xmp => 1,
9708     iframe => 1,
9709     noembed => 1,
9710 wakaba 1.145 noframes => 1, ## NOTE: This is an "as if in head" code clone.
9711 wakaba 1.107 noscript => 0, ## TODO: 1 if scripting is enabled
9712 wakaba 1.102 }->{$token->{tag_name}}) {
9713 wakaba 1.107 if ($token->{tag_name} eq 'xmp') {
9714    
9715     $reconstruct_active_formatting_elements->($insert_to_current);
9716     } else {
9717    
9718 wakaba 1.1 }
9719 wakaba 1.107 ## NOTE: There is an "as if in body" code clone.
9720 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
9721 wakaba 1.123 next B;
9722 wakaba 1.54 } elsif ($token->{tag_name} eq 'isindex') {
9723 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'isindex', token => $token);
9724 wakaba 1.54
9725     if (defined $self->{form_element}) {
9726 wakaba 1.79
9727 wakaba 1.54 ## Ignore the token
9728 wakaba 1.122 ## NOTE: Not acknowledged.
9729 wakaba 1.54 $token = $self->_get_next_token;
9730 wakaba 1.123 next B;
9731 wakaba 1.54 } else {
9732 wakaba 1.144 delete $self->{self_closing};
9733    
9734 wakaba 1.54 my $at = $token->{attributes};
9735     my $form_attrs;
9736     $form_attrs->{action} = $at->{action} if $at->{action};
9737     my $prompt_attr = $at->{prompt};
9738     $at->{name} = {name => 'name', value => 'isindex'};
9739     delete $at->{action};
9740     delete $at->{prompt};
9741     my @tokens = (
9742 wakaba 1.57 {type => START_TAG_TOKEN, tag_name => 'form',
9743 wakaba 1.112 attributes => $form_attrs,
9744     line => $token->{line}, column => $token->{column}},
9745     {type => START_TAG_TOKEN, tag_name => 'hr',
9746     line => $token->{line}, column => $token->{column}},
9747     {type => START_TAG_TOKEN, tag_name => 'p',
9748     line => $token->{line}, column => $token->{column}},
9749     {type => START_TAG_TOKEN, tag_name => 'label',
9750     line => $token->{line}, column => $token->{column}},
9751 wakaba 1.54 );
9752     if ($prompt_attr) {
9753 wakaba 1.79
9754 wakaba 1.112 push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
9755 wakaba 1.116 #line => $token->{line}, column => $token->{column},
9756     };
9757 wakaba 1.1 } else {
9758 wakaba 1.79
9759 wakaba 1.57 push @tokens, {type => CHARACTER_TOKEN,
9760 wakaba 1.112 data => 'This is a searchable index. Insert your search keywords here: ',
9761 wakaba 1.116 #line => $token->{line}, column => $token->{column},
9762     }; # SHOULD
9763 wakaba 1.54 ## TODO: make this configurable
9764 wakaba 1.1 }
9765 wakaba 1.54 push @tokens,
9766 wakaba 1.112 {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
9767     line => $token->{line}, column => $token->{column}},
9768 wakaba 1.57 #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
9769 wakaba 1.112 {type => END_TAG_TOKEN, tag_name => 'label',
9770     line => $token->{line}, column => $token->{column}},
9771     {type => END_TAG_TOKEN, tag_name => 'p',
9772     line => $token->{line}, column => $token->{column}},
9773     {type => START_TAG_TOKEN, tag_name => 'hr',
9774     line => $token->{line}, column => $token->{column}},
9775     {type => END_TAG_TOKEN, tag_name => 'form',
9776     line => $token->{line}, column => $token->{column}};
9777 wakaba 1.54 unshift @{$self->{token}}, (@tokens);
9778 wakaba 1.122 $token = $self->_get_next_token;
9779 wakaba 1.123 next B;
9780 wakaba 1.54 }
9781     } elsif ($token->{tag_name} eq 'textarea') {
9782 wakaba 1.200 ## Step 1
9783 wakaba 1.54
9784 wakaba 1.200 {
9785     my $el;
9786    
9787 wakaba 1.54 $el = $self->{document}->create_element_ns
9788 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
9789 wakaba 1.54
9790 wakaba 1.200 for my $attr_name (keys %{ $token->{attributes}}) {
9791     my $attr_t = $token->{attributes}->{$attr_name};
9792 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9793 wakaba 1.117 $attr->value ($attr_t->{value});
9794     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9795     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9796     $el->set_attribute_node_ns ($attr);
9797 wakaba 1.54 }
9798    
9799 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
9800     if defined $token->{line};
9801     $el->set_user_data (manakai_source_column => $token->{column})
9802     if defined $token->{column};
9803    
9804 wakaba 1.200 $insert->($el);
9805     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9806     }
9807    
9808 wakaba 1.54
9809 wakaba 1.200 ## Step 2
9810 wakaba 1.54 ## TODO: $self->{form_element} if defined
9811 wakaba 1.200
9812     ## Step 3
9813     $self->{ignore_newline} = 1;
9814    
9815     ## Step 4
9816     ## ISSUE: This step is wrong. (r2302 enbugged)
9817    
9818     ## Step 5
9819 wakaba 1.54 $self->{content_model} = RCDATA_CONTENT_MODEL;
9820     delete $self->{escape}; # MUST
9821 wakaba 1.200
9822     ## Step 6-7
9823     $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
9824    
9825 wakaba 1.54
9826     $token = $self->_get_next_token;
9827 wakaba 1.123 next B;
9828 wakaba 1.196 } elsif ($token->{tag_name} eq 'optgroup' or
9829     $token->{tag_name} eq 'option') {
9830     ## has an |option| element in scope
9831     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9832     my $node = $self->{open_elements}->[$_];
9833     if ($node->[1] & OPTION_EL) {
9834    
9835     ## NOTE: As if </option>
9836    
9837     $token->{self_closing} = $self->{self_closing};
9838     unshift @{$self->{token}}, $token;
9839     delete $self->{self_closing};
9840     # <option> or <optgroup>
9841     $token = {type => END_TAG_TOKEN, tag_name => 'option',
9842     line => $token->{line}, column => $token->{column}};
9843     next B;
9844     } elsif ($node->[1] & SCOPING_EL) {
9845    
9846     last INSCOPE;
9847     }
9848     } # INSCOPE
9849    
9850     $reconstruct_active_formatting_elements->($insert_to_current);
9851    
9852    
9853     {
9854     my $el;
9855    
9856     $el = $self->{document}->create_element_ns
9857     ($HTML_NS, [undef, $token->{tag_name}]);
9858    
9859     for my $attr_name (keys %{ $token->{attributes}}) {
9860     my $attr_t = $token->{attributes}->{$attr_name};
9861     my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9862     $attr->value ($attr_t->{value});
9863     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9864     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9865     $el->set_attribute_node_ns ($attr);
9866     }
9867    
9868     $el->set_user_data (manakai_source_line => $token->{line})
9869     if defined $token->{line};
9870     $el->set_user_data (manakai_source_column => $token->{column})
9871     if defined $token->{column};
9872    
9873     $insert->($el);
9874     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9875     }
9876    
9877    
9878    
9879     $token = $self->_get_next_token;
9880     redo B;
9881 wakaba 1.149 } elsif ($token->{tag_name} eq 'rt' or
9882     $token->{tag_name} eq 'rp') {
9883     ## has a |ruby| element in scope
9884     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
9885     my $node = $self->{open_elements}->[$_];
9886     if ($node->[1] & RUBY_EL) {
9887    
9888     ## generate implied end tags
9889     while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
9890    
9891     pop @{$self->{open_elements}};
9892     }
9893     unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
9894    
9895 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
9896     text => $self->{open_elements}->[-1]->[0]
9897 wakaba 1.149 ->manakai_local_name,
9898     token => $token);
9899     pop @{$self->{open_elements}}
9900     while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
9901     }
9902     last INSCOPE;
9903     } elsif ($node->[1] & SCOPING_EL) {
9904    
9905     last INSCOPE;
9906     }
9907     } # INSCOPE
9908    
9909    
9910     {
9911     my $el;
9912    
9913     $el = $self->{document}->create_element_ns
9914     ($HTML_NS, [undef, $token->{tag_name}]);
9915    
9916     for my $attr_name (keys %{ $token->{attributes}}) {
9917     my $attr_t = $token->{attributes}->{$attr_name};
9918     my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
9919     $attr->value ($attr_t->{value});
9920     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9921     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9922     $el->set_attribute_node_ns ($attr);
9923     }
9924    
9925     $el->set_user_data (manakai_source_line => $token->{line})
9926     if defined $token->{line};
9927     $el->set_user_data (manakai_source_column => $token->{column})
9928     if defined $token->{column};
9929    
9930     $insert->($el);
9931     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
9932     }
9933    
9934    
9935    
9936     $token = $self->_get_next_token;
9937     redo B;
9938 wakaba 1.123 } elsif ($token->{tag_name} eq 'math' or
9939     $token->{tag_name} eq 'svg') {
9940     $reconstruct_active_formatting_elements->($insert_to_current);
9941 wakaba 1.128
9942 wakaba 1.152 ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
9943    
9944 wakaba 1.128 ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
9945    
9946     ## "adjust foreign attributes" - done in insert-element-f
9947 wakaba 1.123
9948    
9949     {
9950     my $el;
9951    
9952     $el = $self->{document}->create_element_ns
9953 wakaba 1.128 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, [undef, $token->{tag_name}]);
9954 wakaba 1.123
9955     for my $attr_name (keys %{ $token->{attributes}}) {
9956     my $attr_t = $token->{attributes}->{$attr_name};
9957 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (
9958     @{
9959     $foreign_attr_xname->{$attr_name} ||
9960     [undef, [undef,
9961 wakaba 1.152 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS) eq $SVG_NS ?
9962 wakaba 1.128 ($svg_attr_name->{$attr_name} || $attr_name) :
9963 wakaba 1.152 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS) eq $MML_NS ?
9964     ($attr_name eq 'definitionurl' ?
9965     'definitionURL' : $attr_name) :
9966 wakaba 1.128 $attr_name]]
9967     }
9968     );
9969 wakaba 1.123 $attr->value ($attr_t->{value});
9970     $attr->set_user_data (manakai_source_line => $attr_t->{line});
9971     $attr->set_user_data (manakai_source_column => $attr_t->{column});
9972     $el->set_attribute_node_ns ($attr);
9973     }
9974    
9975     $el->set_user_data (manakai_source_line => $token->{line})
9976     if defined $token->{line};
9977     $el->set_user_data (manakai_source_column => $token->{column})
9978     if defined $token->{column};
9979    
9980     $insert->($el);
9981     push @{$self->{open_elements}}, [$el, ($el_category_f->{$token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS}->{ $token->{tag_name}} || 0) | FOREIGN_EL];
9982 wakaba 1.128
9983     if ( $token->{attributes}->{xmlns} and $token->{attributes}->{xmlns}->{value} ne ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS)) {
9984 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'bad namespace', token => $token);
9985 wakaba 1.128 ## TODO: Error type documentation
9986     }
9987 wakaba 1.123 }
9988    
9989    
9990     if ($self->{self_closing}) {
9991     pop @{$self->{open_elements}};
9992     delete $self->{self_closing};
9993     } else {
9994    
9995     $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
9996     ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
9997     ## mode, "in body" (not "in foreign content") secondary insertion
9998     ## mode, maybe.
9999     }
10000    
10001     $token = $self->_get_next_token;
10002     next B;
10003 wakaba 1.54 } elsif ({
10004     caption => 1, col => 1, colgroup => 1, frame => 1,
10005 wakaba 1.196 frameset => 1, head => 1,
10006 wakaba 1.54 tbody => 1, td => 1, tfoot => 1, th => 1,
10007     thead => 1, tr => 1,
10008     }->{$token->{tag_name}}) {
10009 wakaba 1.79
10010 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'in body',
10011     text => $token->{tag_name}, token => $token);
10012 wakaba 1.54 ## Ignore the token
10013 wakaba 1.122 ## NOTE: |<col/>| or |<frame/>| here is an error.
10014 wakaba 1.54 $token = $self->_get_next_token;
10015 wakaba 1.123 next B;
10016 wakaba 1.193 } elsif ($token->{tag_name} eq 'param' or
10017     $token->{tag_name} eq 'source') {
10018 wakaba 1.54
10019 wakaba 1.193 {
10020     my $el;
10021    
10022     $el = $self->{document}->create_element_ns
10023     ($HTML_NS, [undef, $token->{tag_name}]);
10024    
10025     for my $attr_name (keys %{ $token->{attributes}}) {
10026     my $attr_t = $token->{attributes}->{$attr_name};
10027     my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
10028     $attr->value ($attr_t->{value});
10029     $attr->set_user_data (manakai_source_line => $attr_t->{line});
10030     $attr->set_user_data (manakai_source_column => $attr_t->{column});
10031     $el->set_attribute_node_ns ($attr);
10032     }
10033    
10034     $el->set_user_data (manakai_source_line => $token->{line})
10035     if defined $token->{line};
10036     $el->set_user_data (manakai_source_column => $token->{column})
10037     if defined $token->{column};
10038    
10039     $insert->($el);
10040     push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
10041     }
10042    
10043     pop @{$self->{open_elements}};
10044    
10045     delete $self->{self_closing};
10046     $token = $self->_get_next_token;
10047     redo B;
10048 wakaba 1.54 } else {
10049 wakaba 1.108 if ($token->{tag_name} eq 'image') {
10050    
10051 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'image', token => $token);
10052 wakaba 1.108 $token->{tag_name} = 'img';
10053     } else {
10054    
10055     }
10056    
10057     ## NOTE: There is an "as if <br>" code clone.
10058 wakaba 1.54 $reconstruct_active_formatting_elements->($insert_to_current);
10059 wakaba 1.53
10060 wakaba 1.54
10061     {
10062     my $el;
10063    
10064     $el = $self->{document}->create_element_ns
10065 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
10066 wakaba 1.54
10067     for my $attr_name (keys %{ $token->{attributes}}) {
10068 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
10069 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
10070 wakaba 1.117 $attr->value ($attr_t->{value});
10071     $attr->set_user_data (manakai_source_line => $attr_t->{line});
10072     $attr->set_user_data (manakai_source_column => $attr_t->{column});
10073     $el->set_attribute_node_ns ($attr);
10074 wakaba 1.53 }
10075 wakaba 1.54
10076 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
10077     if defined $token->{line};
10078     $el->set_user_data (manakai_source_column => $token->{column})
10079     if defined $token->{column};
10080    
10081 wakaba 1.54 $insert->($el);
10082 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
10083 wakaba 1.54 }
10084    
10085 wakaba 1.107
10086 wakaba 1.108 if ({
10087     applet => 1, marquee => 1, object => 1,
10088     }->{$token->{tag_name}}) {
10089    
10090     push @$active_formatting_elements, ['#marker', ''];
10091 wakaba 1.122
10092 wakaba 1.108 } elsif ({
10093     b => 1, big => 1, em => 1, font => 1, i => 1,
10094 wakaba 1.188 s => 1, small => 1, strike => 1,
10095 wakaba 1.108 strong => 1, tt => 1, u => 1,
10096     }->{$token->{tag_name}}) {
10097    
10098     push @$active_formatting_elements, $self->{open_elements}->[-1];
10099 wakaba 1.122
10100 wakaba 1.108 } elsif ($token->{tag_name} eq 'input') {
10101    
10102     ## TODO: associate with $self->{form_element} if defined
10103     pop @{$self->{open_elements}};
10104 wakaba 1.122 delete $self->{self_closing};
10105 wakaba 1.108 } elsif ({
10106     area => 1, basefont => 1, bgsound => 1, br => 1,
10107 wakaba 1.193 embed => 1, img => 1, spacer => 1, wbr => 1,
10108 wakaba 1.108 }->{$token->{tag_name}}) {
10109    
10110     pop @{$self->{open_elements}};
10111 wakaba 1.122 delete $self->{self_closing};
10112 wakaba 1.108 } elsif ($token->{tag_name} eq 'select') {
10113 wakaba 1.107 ## TODO: associate with $self->{form_element} if defined
10114    
10115     if ($self->{insertion_mode} & TABLE_IMS or
10116     $self->{insertion_mode} & BODY_TABLE_IMS or
10117     $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
10118    
10119     $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
10120     } else {
10121    
10122     $self->{insertion_mode} = IN_SELECT_IM;
10123     }
10124 wakaba 1.122
10125 wakaba 1.108 } else {
10126    
10127 wakaba 1.107 }
10128 wakaba 1.53
10129 wakaba 1.54 $token = $self->_get_next_token;
10130 wakaba 1.123 next B;
10131 wakaba 1.54 }
10132 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
10133 wakaba 1.54 if ($token->{tag_name} eq 'body') {
10134 wakaba 1.105 ## has a |body| element in scope
10135     my $i;
10136 wakaba 1.109 INSCOPE: {
10137     for (reverse @{$self->{open_elements}}) {
10138 wakaba 1.121 if ($_->[1] & BODY_EL) {
10139 wakaba 1.109
10140     $i = $_;
10141     last INSCOPE;
10142 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
10143 wakaba 1.109
10144     last;
10145     }
10146 wakaba 1.54 }
10147 wakaba 1.109
10148 wakaba 1.195 ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
10149    
10150     $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10151 wakaba 1.150 text => $token->{tag_name}, token => $token);
10152 wakaba 1.105 ## NOTE: Ignore the token.
10153 wakaba 1.54 $token = $self->_get_next_token;
10154 wakaba 1.123 next B;
10155 wakaba 1.109 } # INSCOPE
10156 wakaba 1.105
10157     for (@{$self->{open_elements}}) {
10158 wakaba 1.121 unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
10159 wakaba 1.105
10160 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10161     text => $_->[0]->manakai_local_name,
10162 wakaba 1.120 token => $token);
10163 wakaba 1.105 last;
10164     } else {
10165    
10166     }
10167     }
10168    
10169     $self->{insertion_mode} = AFTER_BODY_IM;
10170     $token = $self->_get_next_token;
10171 wakaba 1.123 next B;
10172 wakaba 1.54 } elsif ($token->{tag_name} eq 'html') {
10173 wakaba 1.120 ## TODO: Update this code. It seems that the code below is not
10174     ## up-to-date, though it has same effect as speced.
10175 wakaba 1.121 if (@{$self->{open_elements}} > 1 and
10176     $self->{open_elements}->[1]->[1] & BODY_EL) {
10177     unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
10178 wakaba 1.79
10179 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10180     text => $self->{open_elements}->[1]->[0]
10181 wakaba 1.120 ->manakai_local_name,
10182     token => $token);
10183 wakaba 1.79 } else {
10184    
10185 wakaba 1.54 }
10186 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
10187 wakaba 1.54 ## reprocess
10188 wakaba 1.123 next B;
10189 wakaba 1.54 } else {
10190 wakaba 1.79
10191 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10192     text => $token->{tag_name}, token => $token);
10193 wakaba 1.54 ## Ignore the token
10194     $token = $self->_get_next_token;
10195 wakaba 1.123 next B;
10196 wakaba 1.53 }
10197 wakaba 1.54 } elsif ({
10198 wakaba 1.190 ## NOTE: End tags for non-phrasing flow content elements
10199    
10200     ## NOTE: The normal ones
10201     address => 1, article => 1, aside => 1, blockquote => 1,
10202     center => 1, datagrid => 1, details => 1, dialog => 1,
10203     dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
10204     footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
10205     ol => 1, pre => 1, section => 1, ul => 1,
10206    
10207     ## NOTE: As normal, but ... optional tags
10208 wakaba 1.54 dd => 1, dt => 1, li => 1,
10209 wakaba 1.190
10210 wakaba 1.102 applet => 1, button => 1, marquee => 1, object => 1,
10211 wakaba 1.54 }->{$token->{tag_name}}) {
10212 wakaba 1.192 ## NOTE: Code for <li> start tags includes "as if </li>" code.
10213     ## Code for <dt> or <dd> start tags includes "as if </dt> or
10214     ## </dd>" code.
10215    
10216 wakaba 1.54 ## has an element in scope
10217     my $i;
10218     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
10219     my $node = $self->{open_elements}->[$_];
10220 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
10221 wakaba 1.79
10222 wakaba 1.54 $i = $_;
10223 wakaba 1.87 last INSCOPE;
10224 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10225 wakaba 1.79
10226 wakaba 1.54 last INSCOPE;
10227     }
10228     } # INSCOPE
10229 wakaba 1.89
10230     unless (defined $i) { # has an element in scope
10231    
10232 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10233     text => $token->{tag_name}, token => $token);
10234 wakaba 1.154 ## NOTE: Ignore the token.
10235 wakaba 1.89 } else {
10236     ## Step 1. generate implied end tags
10237     while ({
10238 wakaba 1.149 ## END_TAG_OPTIONAL_EL
10239 wakaba 1.89 dd => ($token->{tag_name} ne 'dd'),
10240     dt => ($token->{tag_name} ne 'dt'),
10241     li => ($token->{tag_name} ne 'li'),
10242 wakaba 1.189 option => 1,
10243     optgroup => 1,
10244 wakaba 1.89 p => 1,
10245 wakaba 1.149 rt => 1,
10246     rp => 1,
10247 wakaba 1.121 }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
10248 wakaba 1.89
10249     pop @{$self->{open_elements}};
10250     }
10251    
10252     ## Step 2.
10253 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10254     ne $token->{tag_name}) {
10255 wakaba 1.79
10256 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10257     text => $self->{open_elements}->[-1]->[0]
10258 wakaba 1.120 ->manakai_local_name,
10259     token => $token);
10260 wakaba 1.1 } else {
10261 wakaba 1.79
10262 wakaba 1.1 }
10263 wakaba 1.89
10264     ## Step 3.
10265 wakaba 1.54 splice @{$self->{open_elements}}, $i;
10266 wakaba 1.89
10267     ## Step 4.
10268     $clear_up_to_marker->()
10269     if {
10270 wakaba 1.102 applet => 1, button => 1, marquee => 1, object => 1,
10271 wakaba 1.89 }->{$token->{tag_name}};
10272 wakaba 1.54 }
10273     $token = $self->_get_next_token;
10274 wakaba 1.123 next B;
10275 wakaba 1.54 } elsif ($token->{tag_name} eq 'form') {
10276 wakaba 1.190 ## NOTE: As normal, but interacts with the form element pointer
10277    
10278 wakaba 1.92 undef $self->{form_element};
10279    
10280 wakaba 1.54 ## has an element in scope
10281 wakaba 1.92 my $i;
10282 wakaba 1.54 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
10283     my $node = $self->{open_elements}->[$_];
10284 wakaba 1.121 if ($node->[1] & FORM_EL) {
10285 wakaba 1.79
10286 wakaba 1.92 $i = $_;
10287 wakaba 1.54 last INSCOPE;
10288 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10289 wakaba 1.79
10290 wakaba 1.54 last INSCOPE;
10291 wakaba 1.36 }
10292 wakaba 1.54 } # INSCOPE
10293 wakaba 1.92
10294     unless (defined $i) { # has an element in scope
10295 wakaba 1.79
10296 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10297     text => $token->{tag_name}, token => $token);
10298 wakaba 1.154 ## NOTE: Ignore the token.
10299 wakaba 1.54 } else {
10300 wakaba 1.92 ## Step 1. generate implied end tags
10301 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
10302 wakaba 1.92
10303     pop @{$self->{open_elements}};
10304     }
10305    
10306     ## Step 2.
10307 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10308     ne $token->{tag_name}) {
10309 wakaba 1.92
10310 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10311     text => $self->{open_elements}->[-1]->[0]
10312 wakaba 1.120 ->manakai_local_name,
10313     token => $token);
10314 wakaba 1.92 } else {
10315    
10316     }
10317 wakaba 1.79
10318 wakaba 1.92 ## Step 3.
10319     splice @{$self->{open_elements}}, $i;
10320 wakaba 1.36 }
10321    
10322 wakaba 1.54 $token = $self->_get_next_token;
10323 wakaba 1.123 next B;
10324 wakaba 1.54 } elsif ({
10325 wakaba 1.190 ## NOTE: As normal, except acts as a closer for any ...
10326 wakaba 1.54 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
10327     }->{$token->{tag_name}}) {
10328     ## has an element in scope
10329     my $i;
10330     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
10331     my $node = $self->{open_elements}->[$_];
10332 wakaba 1.121 if ($node->[1] & HEADING_EL) {
10333 wakaba 1.79
10334 wakaba 1.54 $i = $_;
10335     last INSCOPE;
10336 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10337 wakaba 1.79
10338 wakaba 1.54 last INSCOPE;
10339 wakaba 1.53 }
10340 wakaba 1.54 } # INSCOPE
10341 wakaba 1.93
10342     unless (defined $i) { # has an element in scope
10343 wakaba 1.79
10344 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10345     text => $token->{tag_name}, token => $token);
10346 wakaba 1.154 ## NOTE: Ignore the token.
10347 wakaba 1.79 } else {
10348 wakaba 1.93 ## Step 1. generate implied end tags
10349 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
10350 wakaba 1.93
10351     pop @{$self->{open_elements}};
10352     }
10353 wakaba 1.79
10354 wakaba 1.93 ## Step 2.
10355 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
10356     ne $token->{tag_name}) {
10357 wakaba 1.93
10358 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10359     text => $token->{tag_name}, token => $token);
10360 wakaba 1.93 } else {
10361    
10362     }
10363    
10364     ## Step 3.
10365     splice @{$self->{open_elements}}, $i;
10366 wakaba 1.53 }
10367    
10368 wakaba 1.54 $token = $self->_get_next_token;
10369 wakaba 1.123 next B;
10370 wakaba 1.87 } elsif ($token->{tag_name} eq 'p') {
10371 wakaba 1.190 ## NOTE: As normal, except </p> implies <p> and ...
10372    
10373 wakaba 1.87 ## has an element in scope
10374 wakaba 1.192 my $non_optional;
10375 wakaba 1.87 my $i;
10376     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
10377     my $node = $self->{open_elements}->[$_];
10378 wakaba 1.121 if ($node->[1] & P_EL) {
10379 wakaba 1.87
10380     $i = $_;
10381 wakaba 1.88 last INSCOPE;
10382 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
10383 wakaba 1.87
10384     last INSCOPE;
10385 wakaba 1.192 } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
10386     ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
10387    
10388     #
10389     } else {
10390    
10391     $non_optional ||= $node;
10392     #
10393 wakaba 1.87 }
10394     } # INSCOPE
10395 wakaba 1.91
10396     if (defined $i) {
10397 wakaba 1.192 ## 1. Generate implied end tags
10398     #
10399    
10400     ## 2. If current node != "p", parse error
10401     if ($non_optional) {
10402 wakaba 1.87
10403 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10404 wakaba 1.192 text => $non_optional->[0]->manakai_local_name,
10405 wakaba 1.120 token => $token);
10406 wakaba 1.87 } else {
10407    
10408     }
10409 wakaba 1.91
10410 wakaba 1.192 ## 3. Pop
10411 wakaba 1.87 splice @{$self->{open_elements}}, $i;
10412     } else {
10413    
10414 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10415     text => $token->{tag_name}, token => $token);
10416 wakaba 1.91
10417    
10418 wakaba 1.87 ## As if <p>, then reprocess the current token
10419     my $el;
10420    
10421     $el = $self->{document}->create_element_ns
10422 wakaba 1.128 ($HTML_NS, [undef, 'p']);
10423 wakaba 1.87
10424 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
10425     if defined $token->{line};
10426     $el->set_user_data (manakai_source_column => $token->{column})
10427     if defined $token->{column};
10428    
10429 wakaba 1.87 $insert->($el);
10430 wakaba 1.91 ## NOTE: Not inserted into |$self->{open_elements}|.
10431 wakaba 1.87 }
10432 wakaba 1.91
10433 wakaba 1.87 $token = $self->_get_next_token;
10434 wakaba 1.123 next B;
10435 wakaba 1.54 } elsif ({
10436     a => 1,
10437     b => 1, big => 1, em => 1, font => 1, i => 1,
10438 wakaba 1.188 nobr => 1, s => 1, small => 1, strike => 1,
10439 wakaba 1.54 strong => 1, tt => 1, u => 1,
10440     }->{$token->{tag_name}}) {
10441 wakaba 1.79
10442 wakaba 1.111 $formatting_end_tag->($token);
10443 wakaba 1.123 next B;
10444 wakaba 1.54 } elsif ($token->{tag_name} eq 'br') {
10445 wakaba 1.79
10446 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10447     text => 'br', token => $token);
10448 wakaba 1.53
10449 wakaba 1.54 ## As if <br>
10450     $reconstruct_active_formatting_elements->($insert_to_current);
10451    
10452     my $el;
10453    
10454 wakaba 1.1 $el = $self->{document}->create_element_ns
10455 wakaba 1.128 ($HTML_NS, [undef, 'br']);
10456 wakaba 1.1
10457 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
10458     if defined $token->{line};
10459     $el->set_user_data (manakai_source_column => $token->{column})
10460     if defined $token->{column};
10461    
10462 wakaba 1.54 $insert->($el);
10463    
10464     ## Ignore the token.
10465     $token = $self->_get_next_token;
10466 wakaba 1.123 next B;
10467 wakaba 1.54 } else {
10468 wakaba 1.190 if ($token->{tag_name} eq 'sarcasm') {
10469     sleep 0.001; # take a deep breath
10470     }
10471    
10472 wakaba 1.54 ## Step 1
10473     my $node_i = -1;
10474     my $node = $self->{open_elements}->[$node_i];
10475    
10476     ## Step 2
10477     S2: {
10478 wakaba 1.195 my $node_tag_name = $node->[0]->manakai_local_name;
10479     $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
10480     if ($node_tag_name eq $token->{tag_name}) {
10481 wakaba 1.54 ## Step 1
10482     ## generate implied end tags
10483 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
10484 wakaba 1.79
10485 wakaba 1.149 ## NOTE: |<ruby><rt></ruby>|.
10486     ## ISSUE: <ruby><rt></rt> will also take this code path,
10487     ## which seems wrong.
10488 wakaba 1.86 pop @{$self->{open_elements}};
10489 wakaba 1.149 $node_i++;
10490 wakaba 1.54 }
10491    
10492     ## Step 2
10493 wakaba 1.195 my $current_tag_name
10494     = $self->{open_elements}->[-1]->[0]->manakai_local_name;
10495     $current_tag_name =~ tr/A-Z/a-z/;
10496     if ($current_tag_name ne $token->{tag_name}) {
10497 wakaba 1.79
10498 wakaba 1.60 ## NOTE: <x><y></x>
10499 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'not closed',
10500     text => $self->{open_elements}->[-1]->[0]
10501 wakaba 1.120 ->manakai_local_name,
10502     token => $token);
10503 wakaba 1.79 } else {
10504    
10505 wakaba 1.54 }
10506    
10507     ## Step 3
10508 wakaba 1.149 splice @{$self->{open_elements}}, $node_i if $node_i < 0;
10509 wakaba 1.53
10510 wakaba 1.36 $token = $self->_get_next_token;
10511 wakaba 1.54 last S2;
10512 wakaba 1.1 } else {
10513 wakaba 1.54 ## Step 3
10514 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
10515 wakaba 1.54 #not $phrasing_category->{$node->[1]} and
10516 wakaba 1.121 ($node->[1] & SPECIAL_EL or
10517     $node->[1] & SCOPING_EL)) {
10518 wakaba 1.79
10519 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'unmatched end tag',
10520     text => $token->{tag_name}, token => $token);
10521 wakaba 1.54 ## Ignore the token
10522     $token = $self->_get_next_token;
10523     last S2;
10524 wakaba 1.188
10525     ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
10526     ## 9.27, "a" is a child of <dd> (conforming). In
10527     ## Firefox 3.0.2, "a" is a child of <body>. In WinIE 7,
10528     ## "a" is a child of both <body> and <dd>.
10529 wakaba 1.54 }
10530 wakaba 1.188
10531 wakaba 1.79
10532 wakaba 1.1 }
10533 wakaba 1.54
10534     ## Step 4
10535     $node_i--;
10536     $node = $self->{open_elements}->[$node_i];
10537    
10538     ## Step 5;
10539     redo S2;
10540     } # S2
10541 wakaba 1.123 next B;
10542 wakaba 1.1 }
10543     }
10544 wakaba 1.123 next B;
10545     } continue { # B
10546     if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
10547     ## NOTE: The code below is executed in cases where it does not have
10548     ## to be, but it it is harmless even in those cases.
10549     ## has an element in scope
10550     INSCOPE: {
10551     for (reverse 0..$#{$self->{open_elements}}) {
10552     my $node = $self->{open_elements}->[$_];
10553     if ($node->[1] & FOREIGN_EL) {
10554     last INSCOPE;
10555     } elsif ($node->[1] & SCOPING_EL) {
10556     last;
10557     }
10558     }
10559    
10560     ## NOTE: No foreign element in scope.
10561     $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
10562     } # INSCOPE
10563     }
10564 wakaba 1.1 } # B
10565    
10566     ## Stop parsing # MUST
10567    
10568     ## TODO: script stuffs
10569 wakaba 1.3 } # _tree_construct_main
10570    
10571 wakaba 1.173 sub set_inner_html ($$$$;$) {
10572 wakaba 1.3 my $class = shift;
10573     my $node = shift;
10574 wakaba 1.173 #my $s = \$_[0];
10575 wakaba 1.3 my $onerror = $_[1];
10576 wakaba 1.158 my $get_wrapper = $_[2] || sub ($) { return $_[0] };
10577 wakaba 1.3
10578 wakaba 1.65 ## ISSUE: Should {confident} be true?
10579    
10580 wakaba 1.3 my $nt = $node->node_type;
10581     if ($nt == 9) {
10582     # MUST
10583    
10584     ## Step 1 # MUST
10585     ## TODO: If the document has an active parser, ...
10586     ## ISSUE: There is an issue in the spec.
10587    
10588     ## Step 2 # MUST
10589     my @cn = @{$node->child_nodes};
10590     for (@cn) {
10591     $node->remove_child ($_);
10592     }
10593    
10594     ## Step 3, 4, 5 # MUST
10595 wakaba 1.173 $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
10596 wakaba 1.3 } elsif ($nt == 1) {
10597     ## TODO: If non-html element
10598    
10599     ## NOTE: Most of this code is copied from |parse_string|
10600    
10601 wakaba 1.158 ## TODO: Support for $get_wrapper
10602    
10603 wakaba 1.3 ## Step 1 # MUST
10604 wakaba 1.14 my $this_doc = $node->owner_document;
10605     my $doc = $this_doc->implementation->create_document;
10606 wakaba 1.18 $doc->manakai_is_html (1);
10607 wakaba 1.3 my $p = $class->new;
10608     $p->{document} = $doc;
10609    
10610 wakaba 1.84 ## Step 8 # MUST
10611 wakaba 1.3 my $i = 0;
10612 wakaba 1.119 $p->{line_prev} = $p->{line} = 1;
10613     $p->{column_prev} = $p->{column} = 0;
10614 wakaba 1.173 require Whatpm::Charset::DecodeHandle;
10615     my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
10616     $input = $get_wrapper->($input);
10617 wakaba 1.179 $p->{set_nc} = sub {
10618 wakaba 1.3 my $self = shift;
10619 wakaba 1.14
10620 wakaba 1.174 my $char = '';
10621 wakaba 1.179 if (defined $self->{next_nc}) {
10622     $char = $self->{next_nc};
10623     delete $self->{next_nc};
10624     $self->{nc} = ord $char;
10625 wakaba 1.173 } else {
10626 wakaba 1.176 $self->{char_buffer} = '';
10627     $self->{char_buffer_pos} = 0;
10628    
10629     my $count = $input->manakai_read_until
10630 wakaba 1.178 ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
10631     $self->{char_buffer_pos});
10632 wakaba 1.176 if ($count) {
10633     $self->{line_prev} = $self->{line};
10634     $self->{column_prev} = $self->{column};
10635     $self->{column}++;
10636 wakaba 1.179 $self->{nc}
10637 wakaba 1.176 = ord substr ($self->{char_buffer},
10638     $self->{char_buffer_pos}++, 1);
10639     return;
10640     }
10641    
10642 wakaba 1.174 if ($input->read ($char, 1)) {
10643 wakaba 1.179 $self->{nc} = ord $char;
10644 wakaba 1.174 } else {
10645 wakaba 1.179 $self->{nc} = -1;
10646 wakaba 1.174 return;
10647     }
10648 wakaba 1.173 }
10649 wakaba 1.119
10650     ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
10651     $p->{column}++;
10652 wakaba 1.4
10653 wakaba 1.179 if ($self->{nc} == 0x000A) { # LF
10654 wakaba 1.119 $p->{line}++;
10655     $p->{column} = 0;
10656 wakaba 1.79
10657 wakaba 1.179 } elsif ($self->{nc} == 0x000D) { # CR
10658 wakaba 1.173 ## TODO: support for abort/streaming
10659 wakaba 1.174 my $next = '';
10660     if ($input->read ($next, 1) and $next ne "\x0A") {
10661 wakaba 1.179 $self->{next_nc} = $next;
10662 wakaba 1.173 }
10663 wakaba 1.179 $self->{nc} = 0x000A; # LF # MUST
10664 wakaba 1.119 $p->{line}++;
10665     $p->{column} = 0;
10666 wakaba 1.79
10667 wakaba 1.179 } elsif ($self->{nc} == 0x0000) { # NULL
10668 wakaba 1.79
10669 wakaba 1.150 $self->{parse_error}->(level => $self->{level}->{must}, type => 'NULL');
10670 wakaba 1.179 $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
10671 wakaba 1.3 }
10672     };
10673 wakaba 1.167
10674 wakaba 1.168 $p->{read_until} = sub {
10675 wakaba 1.173 #my ($scalar, $specials_range, $offset) = @_;
10676 wakaba 1.179 return 0 if defined $p->{next_nc};
10677 wakaba 1.176
10678 wakaba 1.178 my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
10679 wakaba 1.176 my $offset = $_[2] || 0;
10680    
10681     if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
10682     pos ($p->{char_buffer}) = $p->{char_buffer_pos};
10683     if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
10684     substr ($_[0], $offset)
10685     = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
10686     my $count = $+[0] - $-[0];
10687     if ($count) {
10688     $p->{column} += $count;
10689     $p->{char_buffer_pos} += $count;
10690     $p->{line_prev} = $p->{line};
10691     $p->{column_prev} = $p->{column} - 1;
10692 wakaba 1.179 $p->{nc} = -1;
10693 wakaba 1.176 }
10694     return $count;
10695     } else {
10696     return 0;
10697     }
10698     } else {
10699     my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
10700     if ($count) {
10701     $p->{column} += $count;
10702     $p->{column_prev} += $count;
10703 wakaba 1.179 $p->{nc} = -1;
10704 wakaba 1.176 }
10705     return $count;
10706 wakaba 1.173 }
10707     }; # $p->{read_until}
10708 wakaba 1.167
10709 wakaba 1.3 my $ponerror = $onerror || sub {
10710     my (%opt) = @_;
10711 wakaba 1.119 my $line = $opt{line};
10712     my $column = $opt{column};
10713     if (defined $opt{token} and defined $opt{token}->{line}) {
10714     $line = $opt{token}->{line};
10715     $column = $opt{token}->{column};
10716     }
10717     warn "Parse error ($opt{type}) at line $line column $column\n";
10718 wakaba 1.3 };
10719     $p->{parse_error} = sub {
10720 wakaba 1.119 $ponerror->(line => $p->{line}, column => $p->{column}, @_);
10721 wakaba 1.3 };
10722    
10723 wakaba 1.174 my $char_onerror = sub {
10724     my (undef, $type, %opt) = @_;
10725     $ponerror->(layer => 'encode',
10726     line => $p->{line}, column => $p->{column} + 1,
10727     %opt, type => $type);
10728     }; # $char_onerror
10729     $input->onerror ($char_onerror);
10730    
10731 wakaba 1.3 $p->_initialize_tokenizer;
10732     $p->_initialize_tree_constructor;
10733    
10734     ## Step 2
10735 wakaba 1.71 my $node_ln = $node->manakai_local_name;
10736 wakaba 1.41 $p->{content_model} = {
10737     title => RCDATA_CONTENT_MODEL,
10738     textarea => RCDATA_CONTENT_MODEL,
10739     style => CDATA_CONTENT_MODEL,
10740     script => CDATA_CONTENT_MODEL,
10741     xmp => CDATA_CONTENT_MODEL,
10742     iframe => CDATA_CONTENT_MODEL,
10743     noembed => CDATA_CONTENT_MODEL,
10744     noframes => CDATA_CONTENT_MODEL,
10745     noscript => CDATA_CONTENT_MODEL,
10746     plaintext => PLAINTEXT_CONTENT_MODEL,
10747     }->{$node_ln};
10748     $p->{content_model} = PCDATA_CONTENT_MODEL
10749     unless defined $p->{content_model};
10750     ## ISSUE: What is "the name of the element"? local name?
10751 wakaba 1.3
10752 wakaba 1.121 $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
10753     ## TODO: Foreign element OK?
10754 wakaba 1.3
10755 wakaba 1.84 ## Step 3
10756 wakaba 1.3 my $root = $doc->create_element_ns
10757     ('http://www.w3.org/1999/xhtml', [undef, 'html']);
10758    
10759 wakaba 1.84 ## Step 4 # MUST
10760 wakaba 1.3 $doc->append_child ($root);
10761    
10762 wakaba 1.84 ## Step 5 # MUST
10763 wakaba 1.121 push @{$p->{open_elements}}, [$root, $el_category->{html}];
10764 wakaba 1.3
10765     undef $p->{head_element};
10766 wakaba 1.197 undef $p->{head_element_inserted};
10767 wakaba 1.3
10768 wakaba 1.84 ## Step 6 # MUST
10769 wakaba 1.3 $p->_reset_insertion_mode;
10770    
10771 wakaba 1.84 ## Step 7 # MUST
10772 wakaba 1.3 my $anode = $node;
10773     AN: while (defined $anode) {
10774     if ($anode->node_type == 1) {
10775     my $nsuri = $anode->namespace_uri;
10776     if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
10777 wakaba 1.71 if ($anode->manakai_local_name eq 'form') {
10778 wakaba 1.79
10779 wakaba 1.3 $p->{form_element} = $anode;
10780     last AN;
10781     }
10782     }
10783     }
10784     $anode = $anode->parent_node;
10785     } # AN
10786    
10787 wakaba 1.84 ## Step 9 # MUST
10788 wakaba 1.3 {
10789     my $self = $p;
10790     $token = $self->_get_next_token;
10791     }
10792     $p->_tree_construction_main;
10793    
10794 wakaba 1.84 ## Step 10 # MUST
10795 wakaba 1.3 my @cn = @{$node->child_nodes};
10796     for (@cn) {
10797     $node->remove_child ($_);
10798     }
10799     ## ISSUE: mutation events? read-only?
10800    
10801 wakaba 1.84 ## Step 11 # MUST
10802 wakaba 1.3 @cn = @{$root->child_nodes};
10803     for (@cn) {
10804 wakaba 1.14 $this_doc->adopt_node ($_);
10805 wakaba 1.3 $node->append_child ($_);
10806     }
10807 wakaba 1.14 ## ISSUE: mutation events?
10808 wakaba 1.3
10809     $p->_terminate_tree_constructor;
10810 wakaba 1.119
10811     delete $p->{parse_error}; # delete loop
10812 wakaba 1.3 } else {
10813     die "$0: |set_inner_html| is not defined for node of type $nt";
10814     }
10815     } # set_inner_html
10816    
10817     } # tree construction stage
10818 wakaba 1.1
10819 wakaba 1.65 package Whatpm::HTML::RestartParser;
10820     push our @ISA, 'Error';
10821    
10822 wakaba 1.1 1;
10823 wakaba 1.200 # $Date: 2008/10/05 05:59:35 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24