/[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.140 - (hide annotations) (download)
Sat May 24 10:48:57 2008 UTC (18 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.139: +76 -81 lines
++ whatpm/Whatpm/ChangeLog	24 May 2008 10:48:45 -0000
	* HTML.pm.src: Ignore language part of public identifiers for
	quriks mode detection (HTML5 revision 1679).

2008-05-24  Wakaba  <wakaba@suika.fam.cx>

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24