/[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.144 - (hide annotations) (download)
Sun May 25 06:03:56 2008 UTC (18 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.143: +4 -3 lines
++ whatpm/t/ChangeLog	25 May 2008 05:59:46 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* tree-test-1.dat: Test data on <isindex/> are added (HTML5
	revision 1697).

++ whatpm/Whatpm/ChangeLog	25 May 2008 06:00:14 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm.src: Don't raise parse error for <isindex/> (HTML5
	revision 1697).

++ whatpm/Whatpm/ContentChecker/ChangeLog	25 May 2008 02:05:07 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: The |param| element is in LC phase now.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24