/[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.137 - (hide annotations) (download)
Sat May 24 09:59:52 2008 UTC (18 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.136: +29 -24 lines
++ whatpm/Whatpm/ChangeLog	24 May 2008 09:59:36 -0000
	* HTML.pm.src (_reset_insertion_mode): Make <td>.innerHTML
	work (HTML5 revision 1690).

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

1 wakaba 1.2 package Whatpm::HTML;
2 wakaba 1.1 use strict;
3 wakaba 1.137 our $VERSION=do{my @r=(q$Revision: 1.139 $=~/\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     } else {
2155    
2156     $self->{parse_error}->(level => $self->{must_level}, type => 'no space between attributes');
2157     $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2158     ## reconsume
2159     redo A;
2160     }
2161     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2162     if ($self->{next_char} == 0x003E) { # >
2163     if ($self->{current_token}->{type} == END_TAG_TOKEN) {
2164 wakaba 1.77
2165 wakaba 1.122 $self->{parse_error}->(level => $self->{must_level}, type => 'nestc', token => $self->{current_token});
2166     ## TODO: Different type than slash in start tag
2167     $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2168     if ($self->{current_token}->{attributes}) {
2169    
2170     $self->{parse_error}->(level => $self->{must_level}, type => 'end tag attribute');
2171     } else {
2172    
2173     }
2174     ## TODO: Test |<title></title/>|
2175 wakaba 1.72 } else {
2176 wakaba 1.77
2177 wakaba 1.122 $self->{self_closing} = 1;
2178 wakaba 1.72 }
2179 wakaba 1.122
2180     $self->{state} = DATA_STATE;
2181    
2182     if (@{$self->{char}}) {
2183     $self->{next_char} = shift @{$self->{char}};
2184     } else {
2185     $self->{set_next_char}->($self);
2186     }
2187    
2188    
2189     return ($self->{current_token}); # start tag or end tag
2190    
2191 wakaba 1.72 redo A;
2192     } else {
2193 wakaba 1.77
2194 wakaba 1.122 $self->{parse_error}->(level => $self->{must_level}, type => 'nestc');
2195     ## TODO: This error type is wrong.
2196 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2197 wakaba 1.122 ## Reconsume.
2198 wakaba 1.72 redo A;
2199     }
2200 wakaba 1.59 } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2201 wakaba 1.1 ## (only happen if PCDATA state)
2202    
2203 wakaba 1.110 ## NOTE: Set by the previous state
2204     #my $token = {type => COMMENT_TOKEN, data => ''};
2205 wakaba 1.1
2206     BC: {
2207 wakaba 1.76 if ($self->{next_char} == 0x003E) { # >
2208 wakaba 1.77
2209 wakaba 1.59 $self->{state} = DATA_STATE;
2210 wakaba 1.1
2211     if (@{$self->{char}}) {
2212 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2213 wakaba 1.1 } else {
2214 wakaba 1.76 $self->{set_next_char}->($self);
2215 wakaba 1.1 }
2216    
2217    
2218 wakaba 1.110 return ($self->{current_token}); # comment
2219 wakaba 1.1
2220     redo A;
2221 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2222 wakaba 1.77
2223 wakaba 1.59 $self->{state} = DATA_STATE;
2224 wakaba 1.1 ## reconsume
2225    
2226 wakaba 1.110 return ($self->{current_token}); # comment
2227 wakaba 1.1
2228     redo A;
2229     } else {
2230 wakaba 1.77
2231 wakaba 1.110 $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
2232 wakaba 1.1
2233     if (@{$self->{char}}) {
2234 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2235 wakaba 1.1 } else {
2236 wakaba 1.76 $self->{set_next_char}->($self);
2237 wakaba 1.1 }
2238    
2239     redo BC;
2240     }
2241     } # BC
2242 wakaba 1.77
2243     die "$0: _get_next_token: unexpected case [BC]";
2244 wakaba 1.59 } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2245 wakaba 1.1 ## (only happen if PCDATA state)
2246    
2247 wakaba 1.118 my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);
2248 wakaba 1.110
2249 wakaba 1.1 my @next_char;
2250 wakaba 1.76 push @next_char, $self->{next_char};
2251 wakaba 1.1
2252 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
2253 wakaba 1.1
2254     if (@{$self->{char}}) {
2255 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2256 wakaba 1.1 } else {
2257 wakaba 1.76 $self->{set_next_char}->($self);
2258 wakaba 1.1 }
2259    
2260 wakaba 1.76 push @next_char, $self->{next_char};
2261     if ($self->{next_char} == 0x002D) { # -
2262 wakaba 1.77
2263 wakaba 1.110 $self->{current_token} = {type => COMMENT_TOKEN, data => '',
2264 wakaba 1.118 line => $l, column => $c,
2265 wakaba 1.116 };
2266 wakaba 1.59 $self->{state} = COMMENT_START_STATE;
2267 wakaba 1.1
2268     if (@{$self->{char}}) {
2269 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2270 wakaba 1.1 } else {
2271 wakaba 1.76 $self->{set_next_char}->($self);
2272 wakaba 1.1 }
2273    
2274     redo A;
2275 wakaba 1.77 } else {
2276    
2277 wakaba 1.1 }
2278 wakaba 1.76 } elsif ($self->{next_char} == 0x0044 or # D
2279     $self->{next_char} == 0x0064) { # d
2280 wakaba 1.1
2281     if (@{$self->{char}}) {
2282 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2283 wakaba 1.1 } else {
2284 wakaba 1.76 $self->{set_next_char}->($self);
2285 wakaba 1.1 }
2286    
2287 wakaba 1.76 push @next_char, $self->{next_char};
2288     if ($self->{next_char} == 0x004F or # O
2289     $self->{next_char} == 0x006F) { # o
2290 wakaba 1.1
2291     if (@{$self->{char}}) {
2292 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2293 wakaba 1.1 } else {
2294 wakaba 1.76 $self->{set_next_char}->($self);
2295 wakaba 1.1 }
2296    
2297 wakaba 1.76 push @next_char, $self->{next_char};
2298     if ($self->{next_char} == 0x0043 or # C
2299     $self->{next_char} == 0x0063) { # c
2300 wakaba 1.1
2301     if (@{$self->{char}}) {
2302 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2303 wakaba 1.1 } else {
2304 wakaba 1.76 $self->{set_next_char}->($self);
2305 wakaba 1.1 }
2306    
2307 wakaba 1.76 push @next_char, $self->{next_char};
2308     if ($self->{next_char} == 0x0054 or # T
2309     $self->{next_char} == 0x0074) { # t
2310 wakaba 1.1
2311     if (@{$self->{char}}) {
2312 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2313 wakaba 1.1 } else {
2314 wakaba 1.76 $self->{set_next_char}->($self);
2315 wakaba 1.1 }
2316    
2317 wakaba 1.76 push @next_char, $self->{next_char};
2318     if ($self->{next_char} == 0x0059 or # Y
2319     $self->{next_char} == 0x0079) { # y
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} == 0x0050 or # P
2329     $self->{next_char} == 0x0070) { # p
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} == 0x0045 or # E
2339     $self->{next_char} == 0x0065) { # e
2340 wakaba 1.77
2341     ## TODO: What a stupid code this is!
2342 wakaba 1.59 $self->{state} = DOCTYPE_STATE;
2343 wakaba 1.110 $self->{current_token} = {type => DOCTYPE_TOKEN,
2344     quirks => 1,
2345 wakaba 1.118 line => $l, column => $c,
2346 wakaba 1.116 };
2347 wakaba 1.1
2348     if (@{$self->{char}}) {
2349 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2350 wakaba 1.1 } else {
2351 wakaba 1.76 $self->{set_next_char}->($self);
2352 wakaba 1.1 }
2353    
2354     redo A;
2355 wakaba 1.77 } else {
2356    
2357 wakaba 1.1 }
2358 wakaba 1.77 } else {
2359    
2360 wakaba 1.1 }
2361 wakaba 1.77 } else {
2362    
2363 wakaba 1.1 }
2364 wakaba 1.77 } else {
2365    
2366 wakaba 1.1 }
2367 wakaba 1.77 } else {
2368    
2369 wakaba 1.1 }
2370 wakaba 1.77 } else {
2371    
2372 wakaba 1.1 }
2373 wakaba 1.124 } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2374     $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2375     $self->{next_char} == 0x005B) { # [
2376    
2377     if (@{$self->{char}}) {
2378     $self->{next_char} = shift @{$self->{char}};
2379     } else {
2380     $self->{set_next_char}->($self);
2381     }
2382    
2383     push @next_char, $self->{next_char};
2384     if ($self->{next_char} == 0x0043) { # C
2385    
2386     if (@{$self->{char}}) {
2387     $self->{next_char} = shift @{$self->{char}};
2388     } else {
2389     $self->{set_next_char}->($self);
2390     }
2391    
2392     push @next_char, $self->{next_char};
2393     if ($self->{next_char} == 0x0044) { # D
2394    
2395     if (@{$self->{char}}) {
2396     $self->{next_char} = shift @{$self->{char}};
2397     } else {
2398     $self->{set_next_char}->($self);
2399     }
2400    
2401     push @next_char, $self->{next_char};
2402     if ($self->{next_char} == 0x0041) { # A
2403    
2404     if (@{$self->{char}}) {
2405     $self->{next_char} = shift @{$self->{char}};
2406     } else {
2407     $self->{set_next_char}->($self);
2408     }
2409    
2410     push @next_char, $self->{next_char};
2411     if ($self->{next_char} == 0x0054) { # T
2412    
2413     if (@{$self->{char}}) {
2414     $self->{next_char} = shift @{$self->{char}};
2415     } else {
2416     $self->{set_next_char}->($self);
2417     }
2418    
2419     push @next_char, $self->{next_char};
2420     if ($self->{next_char} == 0x0041) { # A
2421    
2422     if (@{$self->{char}}) {
2423     $self->{next_char} = shift @{$self->{char}};
2424     } else {
2425     $self->{set_next_char}->($self);
2426     }
2427    
2428     push @next_char, $self->{next_char};
2429     if ($self->{next_char} == 0x005B) { # [
2430    
2431     $self->{state} = CDATA_BLOCK_STATE;
2432    
2433     if (@{$self->{char}}) {
2434     $self->{next_char} = shift @{$self->{char}};
2435     } else {
2436     $self->{set_next_char}->($self);
2437     }
2438    
2439     redo A;
2440     } else {
2441    
2442     }
2443     } else {
2444    
2445     }
2446     } else {
2447    
2448     }
2449     } else {
2450    
2451     }
2452     } else {
2453    
2454     }
2455     } else {
2456    
2457     }
2458 wakaba 1.77 } else {
2459    
2460 wakaba 1.1 }
2461    
2462 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'bogus comment');
2463 wakaba 1.76 $self->{next_char} = shift @next_char;
2464 wakaba 1.1 unshift @{$self->{char}}, (@next_char);
2465 wakaba 1.59 $self->{state} = BOGUS_COMMENT_STATE;
2466 wakaba 1.110 $self->{current_token} = {type => COMMENT_TOKEN, data => '',
2467 wakaba 1.118 line => $l, column => $c,
2468 wakaba 1.116 };
2469 wakaba 1.1 redo A;
2470    
2471     ## ISSUE: typos in spec: chacacters, is is a parse error
2472     ## 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?
2473 wakaba 1.59 } elsif ($self->{state} == COMMENT_START_STATE) {
2474 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
2475 wakaba 1.77
2476 wakaba 1.59 $self->{state} = COMMENT_START_DASH_STATE;
2477 wakaba 1.23
2478     if (@{$self->{char}}) {
2479 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2480 wakaba 1.23 } else {
2481 wakaba 1.76 $self->{set_next_char}->($self);
2482 wakaba 1.23 }
2483    
2484     redo A;
2485 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2486 wakaba 1.77
2487 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'bogus comment');
2488 wakaba 1.59 $self->{state} = DATA_STATE;
2489 wakaba 1.23
2490     if (@{$self->{char}}) {
2491 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2492 wakaba 1.23 } else {
2493 wakaba 1.76 $self->{set_next_char}->($self);
2494 wakaba 1.23 }
2495    
2496    
2497     return ($self->{current_token}); # comment
2498    
2499     redo A;
2500 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2501 wakaba 1.77
2502 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed comment');
2503 wakaba 1.59 $self->{state} = DATA_STATE;
2504 wakaba 1.23 ## reconsume
2505    
2506     return ($self->{current_token}); # comment
2507    
2508 wakaba 1.77 redo A;
2509     } else {
2510    
2511 wakaba 1.23 $self->{current_token}->{data} # comment
2512 wakaba 1.76 .= chr ($self->{next_char});
2513 wakaba 1.59 $self->{state} = COMMENT_STATE;
2514 wakaba 1.23
2515     if (@{$self->{char}}) {
2516 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2517 wakaba 1.23 } else {
2518 wakaba 1.76 $self->{set_next_char}->($self);
2519 wakaba 1.23 }
2520    
2521     redo A;
2522     }
2523 wakaba 1.59 } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2524 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
2525 wakaba 1.77
2526 wakaba 1.59 $self->{state} = COMMENT_END_STATE;
2527 wakaba 1.23
2528     if (@{$self->{char}}) {
2529 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2530 wakaba 1.23 } else {
2531 wakaba 1.76 $self->{set_next_char}->($self);
2532 wakaba 1.23 }
2533    
2534     redo A;
2535 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2536 wakaba 1.77
2537 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'bogus comment');
2538 wakaba 1.59 $self->{state} = DATA_STATE;
2539 wakaba 1.23
2540     if (@{$self->{char}}) {
2541 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2542 wakaba 1.23 } else {
2543 wakaba 1.76 $self->{set_next_char}->($self);
2544 wakaba 1.23 }
2545    
2546    
2547     return ($self->{current_token}); # comment
2548    
2549     redo A;
2550 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2551 wakaba 1.77
2552 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed comment');
2553 wakaba 1.59 $self->{state} = DATA_STATE;
2554 wakaba 1.23 ## reconsume
2555    
2556     return ($self->{current_token}); # comment
2557    
2558     redo A;
2559     } else {
2560 wakaba 1.77
2561 wakaba 1.23 $self->{current_token}->{data} # comment
2562 wakaba 1.76 .= '-' . chr ($self->{next_char});
2563 wakaba 1.59 $self->{state} = COMMENT_STATE;
2564 wakaba 1.23
2565     if (@{$self->{char}}) {
2566 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2567 wakaba 1.23 } else {
2568 wakaba 1.76 $self->{set_next_char}->($self);
2569 wakaba 1.23 }
2570    
2571     redo A;
2572     }
2573 wakaba 1.59 } elsif ($self->{state} == COMMENT_STATE) {
2574 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
2575 wakaba 1.77
2576 wakaba 1.59 $self->{state} = COMMENT_END_DASH_STATE;
2577 wakaba 1.1
2578     if (@{$self->{char}}) {
2579 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2580 wakaba 1.1 } else {
2581 wakaba 1.76 $self->{set_next_char}->($self);
2582 wakaba 1.1 }
2583    
2584     redo A;
2585 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2586 wakaba 1.77
2587 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed comment');
2588 wakaba 1.59 $self->{state} = DATA_STATE;
2589 wakaba 1.1 ## reconsume
2590    
2591     return ($self->{current_token}); # comment
2592    
2593     redo A;
2594     } else {
2595 wakaba 1.77
2596 wakaba 1.76 $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
2597 wakaba 1.1 ## Stay in the state
2598    
2599     if (@{$self->{char}}) {
2600 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2601 wakaba 1.1 } else {
2602 wakaba 1.76 $self->{set_next_char}->($self);
2603 wakaba 1.1 }
2604    
2605     redo A;
2606     }
2607 wakaba 1.59 } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2608 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
2609 wakaba 1.77
2610 wakaba 1.59 $self->{state} = COMMENT_END_STATE;
2611 wakaba 1.1
2612     if (@{$self->{char}}) {
2613 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2614 wakaba 1.1 } else {
2615 wakaba 1.76 $self->{set_next_char}->($self);
2616 wakaba 1.1 }
2617    
2618     redo A;
2619 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2620 wakaba 1.77
2621 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed comment');
2622 wakaba 1.59 $self->{state} = DATA_STATE;
2623 wakaba 1.1 ## reconsume
2624    
2625     return ($self->{current_token}); # comment
2626    
2627     redo A;
2628     } else {
2629 wakaba 1.77
2630 wakaba 1.76 $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment
2631 wakaba 1.59 $self->{state} = COMMENT_STATE;
2632 wakaba 1.1
2633     if (@{$self->{char}}) {
2634 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2635 wakaba 1.1 } else {
2636 wakaba 1.76 $self->{set_next_char}->($self);
2637 wakaba 1.1 }
2638    
2639     redo A;
2640     }
2641 wakaba 1.59 } elsif ($self->{state} == COMMENT_END_STATE) {
2642 wakaba 1.76 if ($self->{next_char} == 0x003E) { # >
2643 wakaba 1.77
2644 wakaba 1.59 $self->{state} = DATA_STATE;
2645 wakaba 1.1
2646     if (@{$self->{char}}) {
2647 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2648 wakaba 1.1 } else {
2649 wakaba 1.76 $self->{set_next_char}->($self);
2650 wakaba 1.1 }
2651    
2652    
2653     return ($self->{current_token}); # comment
2654    
2655     redo A;
2656 wakaba 1.76 } elsif ($self->{next_char} == 0x002D) { # -
2657 wakaba 1.77
2658 wakaba 1.112 $self->{parse_error}->(level => $self->{must_level}, type => 'dash in comment',
2659     line => $self->{line_prev},
2660     column => $self->{column_prev});
2661 wakaba 1.1 $self->{current_token}->{data} .= '-'; # comment
2662     ## Stay in the state
2663    
2664     if (@{$self->{char}}) {
2665 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2666 wakaba 1.1 } else {
2667 wakaba 1.76 $self->{set_next_char}->($self);
2668 wakaba 1.1 }
2669    
2670     redo A;
2671 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2672 wakaba 1.77
2673 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed comment');
2674 wakaba 1.59 $self->{state} = DATA_STATE;
2675 wakaba 1.1 ## reconsume
2676    
2677     return ($self->{current_token}); # comment
2678    
2679     redo A;
2680     } else {
2681 wakaba 1.77
2682 wakaba 1.112 $self->{parse_error}->(level => $self->{must_level}, type => 'dash in comment',
2683     line => $self->{line_prev},
2684     column => $self->{column_prev});
2685 wakaba 1.76 $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment
2686 wakaba 1.59 $self->{state} = COMMENT_STATE;
2687 wakaba 1.1
2688     if (@{$self->{char}}) {
2689 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2690 wakaba 1.1 } else {
2691 wakaba 1.76 $self->{set_next_char}->($self);
2692 wakaba 1.1 }
2693    
2694     redo A;
2695     }
2696 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_STATE) {
2697 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
2698     $self->{next_char} == 0x000A or # LF
2699     $self->{next_char} == 0x000B or # VT
2700     $self->{next_char} == 0x000C or # FF
2701     $self->{next_char} == 0x0020) { # SP
2702 wakaba 1.77
2703 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2704 wakaba 1.1
2705     if (@{$self->{char}}) {
2706 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2707 wakaba 1.1 } else {
2708 wakaba 1.76 $self->{set_next_char}->($self);
2709 wakaba 1.1 }
2710    
2711     redo A;
2712     } else {
2713 wakaba 1.77
2714 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'no space before DOCTYPE name');
2715 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2716 wakaba 1.1 ## reconsume
2717     redo A;
2718     }
2719 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2720 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
2721     $self->{next_char} == 0x000A or # LF
2722     $self->{next_char} == 0x000B or # VT
2723     $self->{next_char} == 0x000C or # FF
2724     $self->{next_char} == 0x0020) { # SP
2725 wakaba 1.77
2726 wakaba 1.1 ## Stay in the state
2727    
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 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2736 wakaba 1.77
2737 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'no DOCTYPE name');
2738 wakaba 1.59 $self->{state} = DATA_STATE;
2739 wakaba 1.18
2740     if (@{$self->{char}}) {
2741 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2742 wakaba 1.18 } else {
2743 wakaba 1.76 $self->{set_next_char}->($self);
2744 wakaba 1.18 }
2745    
2746    
2747 wakaba 1.110 return ($self->{current_token}); # DOCTYPE (quirks)
2748 wakaba 1.18
2749     redo A;
2750 wakaba 1.77 } elsif ($self->{next_char} == -1) {
2751    
2752 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'no DOCTYPE name');
2753 wakaba 1.59 $self->{state} = DATA_STATE;
2754 wakaba 1.18 ## reconsume
2755    
2756 wakaba 1.110 return ($self->{current_token}); # DOCTYPE (quirks)
2757 wakaba 1.18
2758     redo A;
2759     } else {
2760 wakaba 1.77
2761 wakaba 1.110 $self->{current_token}->{name} = chr $self->{next_char};
2762     delete $self->{current_token}->{quirks};
2763 wakaba 1.4 ## ISSUE: "Set the token's name name to the" in the spec
2764 wakaba 1.59 $self->{state} = DOCTYPE_NAME_STATE;
2765 wakaba 1.1
2766     if (@{$self->{char}}) {
2767 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2768 wakaba 1.1 } else {
2769 wakaba 1.76 $self->{set_next_char}->($self);
2770 wakaba 1.1 }
2771    
2772     redo A;
2773 wakaba 1.18 }
2774 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2775 wakaba 1.18 ## ISSUE: Redundant "First," in the spec.
2776 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
2777     $self->{next_char} == 0x000A or # LF
2778     $self->{next_char} == 0x000B or # VT
2779     $self->{next_char} == 0x000C or # FF
2780     $self->{next_char} == 0x0020) { # SP
2781 wakaba 1.77
2782 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2783 wakaba 1.18
2784     if (@{$self->{char}}) {
2785 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2786 wakaba 1.18 } else {
2787 wakaba 1.76 $self->{set_next_char}->($self);
2788 wakaba 1.18 }
2789    
2790     redo A;
2791 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2792 wakaba 1.77
2793 wakaba 1.59 $self->{state} = DATA_STATE;
2794 wakaba 1.1
2795     if (@{$self->{char}}) {
2796 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2797 wakaba 1.1 } else {
2798 wakaba 1.76 $self->{set_next_char}->($self);
2799 wakaba 1.1 }
2800    
2801    
2802 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
2803 wakaba 1.1
2804     redo A;
2805 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2806 wakaba 1.77
2807 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed DOCTYPE');
2808 wakaba 1.59 $self->{state} = DATA_STATE;
2809 wakaba 1.1 ## reconsume
2810    
2811 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2812 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
2813 wakaba 1.1
2814     redo A;
2815     } else {
2816 wakaba 1.77
2817 wakaba 1.18 $self->{current_token}->{name}
2818 wakaba 1.76 .= chr ($self->{next_char}); # DOCTYPE
2819 wakaba 1.18 ## Stay in the state
2820 wakaba 1.1
2821     if (@{$self->{char}}) {
2822 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2823 wakaba 1.1 } else {
2824 wakaba 1.76 $self->{set_next_char}->($self);
2825 wakaba 1.1 }
2826    
2827     redo A;
2828     }
2829 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2830 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
2831     $self->{next_char} == 0x000A or # LF
2832     $self->{next_char} == 0x000B or # VT
2833     $self->{next_char} == 0x000C or # FF
2834     $self->{next_char} == 0x0020) { # SP
2835 wakaba 1.77
2836 wakaba 1.18 ## Stay in the state
2837 wakaba 1.1
2838     if (@{$self->{char}}) {
2839 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2840 wakaba 1.1 } else {
2841 wakaba 1.76 $self->{set_next_char}->($self);
2842 wakaba 1.1 }
2843    
2844     redo A;
2845 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2846 wakaba 1.77
2847 wakaba 1.59 $self->{state} = DATA_STATE;
2848 wakaba 1.1
2849     if (@{$self->{char}}) {
2850 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2851 wakaba 1.1 } else {
2852 wakaba 1.76 $self->{set_next_char}->($self);
2853 wakaba 1.1 }
2854    
2855    
2856     return ($self->{current_token}); # DOCTYPE
2857    
2858     redo A;
2859 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2860 wakaba 1.77
2861 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed DOCTYPE');
2862 wakaba 1.59 $self->{state} = DATA_STATE;
2863 wakaba 1.18 ## reconsume
2864    
2865 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2866 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
2867    
2868     redo A;
2869 wakaba 1.76 } elsif ($self->{next_char} == 0x0050 or # P
2870     $self->{next_char} == 0x0070) { # p
2871 wakaba 1.18
2872     if (@{$self->{char}}) {
2873 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2874 wakaba 1.18 } else {
2875 wakaba 1.76 $self->{set_next_char}->($self);
2876 wakaba 1.18 }
2877    
2878 wakaba 1.76 if ($self->{next_char} == 0x0055 or # U
2879     $self->{next_char} == 0x0075) { # u
2880 wakaba 1.18
2881     if (@{$self->{char}}) {
2882 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2883 wakaba 1.18 } else {
2884 wakaba 1.76 $self->{set_next_char}->($self);
2885 wakaba 1.18 }
2886    
2887 wakaba 1.76 if ($self->{next_char} == 0x0042 or # B
2888     $self->{next_char} == 0x0062) { # b
2889 wakaba 1.18
2890     if (@{$self->{char}}) {
2891 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2892 wakaba 1.18 } else {
2893 wakaba 1.76 $self->{set_next_char}->($self);
2894 wakaba 1.18 }
2895    
2896 wakaba 1.76 if ($self->{next_char} == 0x004C or # L
2897     $self->{next_char} == 0x006C) { # l
2898 wakaba 1.18
2899     if (@{$self->{char}}) {
2900 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2901 wakaba 1.18 } else {
2902 wakaba 1.76 $self->{set_next_char}->($self);
2903 wakaba 1.18 }
2904    
2905 wakaba 1.76 if ($self->{next_char} == 0x0049 or # I
2906     $self->{next_char} == 0x0069) { # i
2907 wakaba 1.18
2908     if (@{$self->{char}}) {
2909 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2910 wakaba 1.18 } else {
2911 wakaba 1.76 $self->{set_next_char}->($self);
2912 wakaba 1.18 }
2913    
2914 wakaba 1.76 if ($self->{next_char} == 0x0043 or # C
2915     $self->{next_char} == 0x0063) { # c
2916 wakaba 1.77
2917 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2918 wakaba 1.18
2919     if (@{$self->{char}}) {
2920 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2921 wakaba 1.18 } else {
2922 wakaba 1.76 $self->{set_next_char}->($self);
2923 wakaba 1.18 }
2924    
2925     redo A;
2926 wakaba 1.77 } else {
2927    
2928 wakaba 1.18 }
2929 wakaba 1.77 } else {
2930    
2931 wakaba 1.18 }
2932 wakaba 1.77 } else {
2933    
2934 wakaba 1.18 }
2935 wakaba 1.77 } else {
2936    
2937 wakaba 1.18 }
2938 wakaba 1.77 } else {
2939    
2940 wakaba 1.18 }
2941    
2942     #
2943 wakaba 1.76 } elsif ($self->{next_char} == 0x0053 or # S
2944     $self->{next_char} == 0x0073) { # s
2945 wakaba 1.18
2946     if (@{$self->{char}}) {
2947 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2948 wakaba 1.18 } else {
2949 wakaba 1.76 $self->{set_next_char}->($self);
2950 wakaba 1.18 }
2951    
2952 wakaba 1.76 if ($self->{next_char} == 0x0059 or # Y
2953     $self->{next_char} == 0x0079) { # y
2954 wakaba 1.18
2955     if (@{$self->{char}}) {
2956 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2957 wakaba 1.18 } else {
2958 wakaba 1.76 $self->{set_next_char}->($self);
2959 wakaba 1.18 }
2960    
2961 wakaba 1.76 if ($self->{next_char} == 0x0053 or # S
2962     $self->{next_char} == 0x0073) { # s
2963 wakaba 1.18
2964     if (@{$self->{char}}) {
2965 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2966 wakaba 1.18 } else {
2967 wakaba 1.76 $self->{set_next_char}->($self);
2968 wakaba 1.18 }
2969    
2970 wakaba 1.76 if ($self->{next_char} == 0x0054 or # T
2971     $self->{next_char} == 0x0074) { # t
2972 wakaba 1.18
2973     if (@{$self->{char}}) {
2974 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2975 wakaba 1.18 } else {
2976 wakaba 1.76 $self->{set_next_char}->($self);
2977 wakaba 1.18 }
2978    
2979 wakaba 1.76 if ($self->{next_char} == 0x0045 or # E
2980     $self->{next_char} == 0x0065) { # e
2981 wakaba 1.18
2982     if (@{$self->{char}}) {
2983 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2984 wakaba 1.18 } else {
2985 wakaba 1.76 $self->{set_next_char}->($self);
2986 wakaba 1.18 }
2987    
2988 wakaba 1.76 if ($self->{next_char} == 0x004D or # M
2989     $self->{next_char} == 0x006D) { # m
2990 wakaba 1.77
2991 wakaba 1.59 $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2992 wakaba 1.18
2993     if (@{$self->{char}}) {
2994 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
2995 wakaba 1.18 } else {
2996 wakaba 1.76 $self->{set_next_char}->($self);
2997 wakaba 1.18 }
2998    
2999     redo A;
3000 wakaba 1.77 } else {
3001    
3002 wakaba 1.18 }
3003 wakaba 1.77 } else {
3004    
3005 wakaba 1.18 }
3006 wakaba 1.77 } else {
3007    
3008 wakaba 1.18 }
3009 wakaba 1.77 } else {
3010    
3011 wakaba 1.18 }
3012 wakaba 1.77 } else {
3013    
3014 wakaba 1.18 }
3015    
3016     #
3017     } else {
3018    
3019 wakaba 1.77
3020 wakaba 1.18 if (@{$self->{char}}) {
3021 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3022 wakaba 1.18 } else {
3023 wakaba 1.76 $self->{set_next_char}->($self);
3024 wakaba 1.18 }
3025    
3026     #
3027     }
3028    
3029 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'string after DOCTYPE name');
3030 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3031 wakaba 1.73
3032 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3033 wakaba 1.18 # next-input-character is already done
3034     redo A;
3035 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
3036 wakaba 1.18 if ({
3037     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
3038     #0x000D => 1, # HT, LF, VT, FF, SP, CR
3039 wakaba 1.76 }->{$self->{next_char}}) {
3040 wakaba 1.77
3041 wakaba 1.1 ## Stay in the state
3042    
3043     if (@{$self->{char}}) {
3044 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3045 wakaba 1.1 } else {
3046 wakaba 1.76 $self->{set_next_char}->($self);
3047 wakaba 1.1 }
3048    
3049     redo A;
3050 wakaba 1.76 } elsif ($self->{next_char} eq 0x0022) { # "
3051 wakaba 1.77
3052 wakaba 1.18 $self->{current_token}->{public_identifier} = ''; # DOCTYPE
3053 wakaba 1.59 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
3054 wakaba 1.18
3055     if (@{$self->{char}}) {
3056 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3057 wakaba 1.18 } else {
3058 wakaba 1.76 $self->{set_next_char}->($self);
3059 wakaba 1.18 }
3060    
3061     redo A;
3062 wakaba 1.76 } elsif ($self->{next_char} eq 0x0027) { # '
3063 wakaba 1.77
3064 wakaba 1.18 $self->{current_token}->{public_identifier} = ''; # DOCTYPE
3065 wakaba 1.59 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
3066 wakaba 1.18
3067     if (@{$self->{char}}) {
3068 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3069 wakaba 1.18 } else {
3070 wakaba 1.76 $self->{set_next_char}->($self);
3071 wakaba 1.18 }
3072    
3073     redo A;
3074 wakaba 1.76 } elsif ($self->{next_char} eq 0x003E) { # >
3075 wakaba 1.77
3076 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'no PUBLIC literal');
3077 wakaba 1.18
3078 wakaba 1.59 $self->{state} = DATA_STATE;
3079 wakaba 1.18
3080     if (@{$self->{char}}) {
3081 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3082 wakaba 1.18 } else {
3083 wakaba 1.76 $self->{set_next_char}->($self);
3084 wakaba 1.18 }
3085    
3086    
3087 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3088 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3089    
3090     redo A;
3091 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3092 wakaba 1.77
3093 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed DOCTYPE');
3094 wakaba 1.18
3095 wakaba 1.59 $self->{state} = DATA_STATE;
3096 wakaba 1.1 ## reconsume
3097    
3098 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3099 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3100 wakaba 1.1
3101     redo A;
3102     } else {
3103 wakaba 1.77
3104 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'string after PUBLIC');
3105 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3106 wakaba 1.73
3107 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3108 wakaba 1.18
3109     if (@{$self->{char}}) {
3110 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3111 wakaba 1.18 } else {
3112 wakaba 1.76 $self->{set_next_char}->($self);
3113 wakaba 1.18 }
3114    
3115     redo A;
3116     }
3117 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
3118 wakaba 1.76 if ($self->{next_char} == 0x0022) { # "
3119 wakaba 1.77
3120 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3121 wakaba 1.18
3122     if (@{$self->{char}}) {
3123 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3124 wakaba 1.18 } else {
3125 wakaba 1.76 $self->{set_next_char}->($self);
3126 wakaba 1.18 }
3127    
3128     redo A;
3129 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3130 wakaba 1.77
3131 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed PUBLIC literal');
3132 wakaba 1.70
3133     $self->{state} = DATA_STATE;
3134    
3135     if (@{$self->{char}}) {
3136 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3137 wakaba 1.70 } else {
3138 wakaba 1.76 $self->{set_next_char}->($self);
3139 wakaba 1.70 }
3140    
3141    
3142 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3143 wakaba 1.70 return ($self->{current_token}); # DOCTYPE
3144    
3145     redo A;
3146 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3147 wakaba 1.77
3148 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed PUBLIC literal');
3149 wakaba 1.18
3150 wakaba 1.59 $self->{state} = DATA_STATE;
3151 wakaba 1.18 ## reconsume
3152    
3153 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3154 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3155    
3156     redo A;
3157     } else {
3158 wakaba 1.77
3159 wakaba 1.18 $self->{current_token}->{public_identifier} # DOCTYPE
3160 wakaba 1.76 .= chr $self->{next_char};
3161 wakaba 1.18 ## Stay in the state
3162    
3163     if (@{$self->{char}}) {
3164 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3165 wakaba 1.18 } else {
3166 wakaba 1.76 $self->{set_next_char}->($self);
3167 wakaba 1.18 }
3168    
3169     redo A;
3170     }
3171 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
3172 wakaba 1.76 if ($self->{next_char} == 0x0027) { # '
3173 wakaba 1.77
3174 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
3175 wakaba 1.18
3176     if (@{$self->{char}}) {
3177 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3178 wakaba 1.18 } else {
3179 wakaba 1.76 $self->{set_next_char}->($self);
3180 wakaba 1.18 }
3181    
3182     redo A;
3183 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3184 wakaba 1.77
3185 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed PUBLIC literal');
3186 wakaba 1.70
3187     $self->{state} = DATA_STATE;
3188    
3189     if (@{$self->{char}}) {
3190 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3191 wakaba 1.70 } else {
3192 wakaba 1.76 $self->{set_next_char}->($self);
3193 wakaba 1.70 }
3194    
3195    
3196 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3197 wakaba 1.70 return ($self->{current_token}); # DOCTYPE
3198    
3199     redo A;
3200 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3201 wakaba 1.77
3202 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed PUBLIC literal');
3203 wakaba 1.18
3204 wakaba 1.59 $self->{state} = DATA_STATE;
3205 wakaba 1.18 ## reconsume
3206    
3207 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3208 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3209    
3210     redo A;
3211     } else {
3212 wakaba 1.77
3213 wakaba 1.18 $self->{current_token}->{public_identifier} # DOCTYPE
3214 wakaba 1.76 .= chr $self->{next_char};
3215 wakaba 1.18 ## Stay in the state
3216    
3217     if (@{$self->{char}}) {
3218 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3219 wakaba 1.18 } else {
3220 wakaba 1.76 $self->{set_next_char}->($self);
3221 wakaba 1.18 }
3222    
3223     redo A;
3224     }
3225 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
3226 wakaba 1.18 if ({
3227     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
3228     #0x000D => 1, # HT, LF, VT, FF, SP, CR
3229 wakaba 1.76 }->{$self->{next_char}}) {
3230 wakaba 1.77
3231 wakaba 1.1 ## Stay in the state
3232    
3233     if (@{$self->{char}}) {
3234 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3235 wakaba 1.1 } else {
3236 wakaba 1.76 $self->{set_next_char}->($self);
3237 wakaba 1.1 }
3238    
3239     redo A;
3240 wakaba 1.76 } elsif ($self->{next_char} == 0x0022) { # "
3241 wakaba 1.77
3242 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
3243 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
3244 wakaba 1.18
3245     if (@{$self->{char}}) {
3246 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3247 wakaba 1.18 } else {
3248 wakaba 1.76 $self->{set_next_char}->($self);
3249 wakaba 1.18 }
3250    
3251     redo A;
3252 wakaba 1.76 } elsif ($self->{next_char} == 0x0027) { # '
3253 wakaba 1.77
3254 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
3255 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
3256 wakaba 1.18
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 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3265 wakaba 1.77
3266 wakaba 1.59 $self->{state} = DATA_STATE;
3267 wakaba 1.18
3268     if (@{$self->{char}}) {
3269 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3270 wakaba 1.18 } else {
3271 wakaba 1.76 $self->{set_next_char}->($self);
3272 wakaba 1.18 }
3273    
3274    
3275     return ($self->{current_token}); # DOCTYPE
3276    
3277     redo A;
3278 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3279 wakaba 1.77
3280 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed DOCTYPE');
3281 wakaba 1.18
3282 wakaba 1.59 $self->{state} = DATA_STATE;
3283 wakaba 1.26 ## reconsume
3284 wakaba 1.18
3285 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3286 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3287    
3288     redo A;
3289     } else {
3290 wakaba 1.77
3291 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'string after PUBLIC literal');
3292 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3293 wakaba 1.73
3294 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3295 wakaba 1.18
3296     if (@{$self->{char}}) {
3297 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3298 wakaba 1.18 } else {
3299 wakaba 1.76 $self->{set_next_char}->($self);
3300 wakaba 1.18 }
3301    
3302     redo A;
3303 wakaba 1.1 }
3304 wakaba 1.59 } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
3305 wakaba 1.18 if ({
3306     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
3307     #0x000D => 1, # HT, LF, VT, FF, SP, CR
3308 wakaba 1.76 }->{$self->{next_char}}) {
3309 wakaba 1.77
3310 wakaba 1.1 ## Stay in the state
3311    
3312     if (@{$self->{char}}) {
3313 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3314 wakaba 1.1 } else {
3315 wakaba 1.76 $self->{set_next_char}->($self);
3316 wakaba 1.1 }
3317    
3318     redo A;
3319 wakaba 1.76 } elsif ($self->{next_char} == 0x0022) { # "
3320 wakaba 1.77
3321 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
3322 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
3323 wakaba 1.18
3324     if (@{$self->{char}}) {
3325 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3326 wakaba 1.18 } else {
3327 wakaba 1.76 $self->{set_next_char}->($self);
3328 wakaba 1.18 }
3329    
3330     redo A;
3331 wakaba 1.76 } elsif ($self->{next_char} == 0x0027) { # '
3332 wakaba 1.77
3333 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
3334 wakaba 1.59 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_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.76 } elsif ($self->{next_char} == 0x003E) { # >
3344 wakaba 1.77
3345 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'no SYSTEM literal');
3346 wakaba 1.59 $self->{state} = DATA_STATE;
3347 wakaba 1.1
3348     if (@{$self->{char}}) {
3349 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3350 wakaba 1.1 } else {
3351 wakaba 1.76 $self->{set_next_char}->($self);
3352 wakaba 1.1 }
3353    
3354    
3355 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3356 wakaba 1.1 return ($self->{current_token}); # DOCTYPE
3357    
3358     redo A;
3359 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3360 wakaba 1.77
3361 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed DOCTYPE');
3362 wakaba 1.18
3363 wakaba 1.59 $self->{state} = DATA_STATE;
3364 wakaba 1.26 ## reconsume
3365 wakaba 1.18
3366 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3367 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3368    
3369     redo A;
3370     } else {
3371 wakaba 1.77
3372 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'string after SYSTEM');
3373 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3374 wakaba 1.73
3375 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3376 wakaba 1.18
3377     if (@{$self->{char}}) {
3378 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3379 wakaba 1.18 } else {
3380 wakaba 1.76 $self->{set_next_char}->($self);
3381 wakaba 1.18 }
3382    
3383     redo A;
3384     }
3385 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
3386 wakaba 1.76 if ($self->{next_char} == 0x0022) { # "
3387 wakaba 1.77
3388 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
3389 wakaba 1.18
3390     if (@{$self->{char}}) {
3391 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3392 wakaba 1.18 } else {
3393 wakaba 1.76 $self->{set_next_char}->($self);
3394 wakaba 1.18 }
3395    
3396     redo A;
3397 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3398 wakaba 1.77
3399 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed PUBLIC literal');
3400 wakaba 1.70
3401     $self->{state} = DATA_STATE;
3402    
3403     if (@{$self->{char}}) {
3404 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3405 wakaba 1.70 } else {
3406 wakaba 1.76 $self->{set_next_char}->($self);
3407 wakaba 1.70 }
3408    
3409    
3410 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3411 wakaba 1.70 return ($self->{current_token}); # DOCTYPE
3412    
3413     redo A;
3414 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3415 wakaba 1.77
3416 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed SYSTEM literal');
3417 wakaba 1.18
3418 wakaba 1.59 $self->{state} = DATA_STATE;
3419 wakaba 1.1 ## reconsume
3420    
3421 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3422 wakaba 1.1 return ($self->{current_token}); # DOCTYPE
3423    
3424     redo A;
3425     } else {
3426 wakaba 1.77
3427 wakaba 1.18 $self->{current_token}->{system_identifier} # DOCTYPE
3428 wakaba 1.76 .= chr $self->{next_char};
3429 wakaba 1.18 ## Stay in the state
3430    
3431     if (@{$self->{char}}) {
3432 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3433 wakaba 1.18 } else {
3434 wakaba 1.76 $self->{set_next_char}->($self);
3435 wakaba 1.18 }
3436    
3437     redo A;
3438     }
3439 wakaba 1.59 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
3440 wakaba 1.76 if ($self->{next_char} == 0x0027) { # '
3441 wakaba 1.77
3442 wakaba 1.59 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
3443 wakaba 1.18
3444     if (@{$self->{char}}) {
3445 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3446 wakaba 1.18 } else {
3447 wakaba 1.76 $self->{set_next_char}->($self);
3448 wakaba 1.18 }
3449    
3450     redo A;
3451 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3452 wakaba 1.77
3453 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed PUBLIC literal');
3454 wakaba 1.70
3455     $self->{state} = DATA_STATE;
3456    
3457     if (@{$self->{char}}) {
3458 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3459 wakaba 1.70 } else {
3460 wakaba 1.76 $self->{set_next_char}->($self);
3461 wakaba 1.70 }
3462    
3463    
3464 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3465 wakaba 1.70 return ($self->{current_token}); # DOCTYPE
3466    
3467     redo A;
3468 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3469 wakaba 1.77
3470 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed SYSTEM literal');
3471 wakaba 1.18
3472 wakaba 1.59 $self->{state} = DATA_STATE;
3473 wakaba 1.18 ## reconsume
3474    
3475 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3476 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3477    
3478     redo A;
3479     } else {
3480 wakaba 1.77
3481 wakaba 1.18 $self->{current_token}->{system_identifier} # DOCTYPE
3482 wakaba 1.76 .= chr $self->{next_char};
3483 wakaba 1.18 ## Stay in the state
3484    
3485     if (@{$self->{char}}) {
3486 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3487 wakaba 1.18 } else {
3488 wakaba 1.76 $self->{set_next_char}->($self);
3489 wakaba 1.18 }
3490    
3491     redo A;
3492     }
3493 wakaba 1.59 } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
3494 wakaba 1.18 if ({
3495     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
3496     #0x000D => 1, # HT, LF, VT, FF, SP, CR
3497 wakaba 1.76 }->{$self->{next_char}}) {
3498 wakaba 1.77
3499 wakaba 1.18 ## Stay in the state
3500    
3501     if (@{$self->{char}}) {
3502 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3503 wakaba 1.18 } else {
3504 wakaba 1.76 $self->{set_next_char}->($self);
3505 wakaba 1.18 }
3506    
3507     redo A;
3508 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
3509 wakaba 1.77
3510 wakaba 1.59 $self->{state} = DATA_STATE;
3511 wakaba 1.18
3512     if (@{$self->{char}}) {
3513 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3514 wakaba 1.18 } else {
3515 wakaba 1.76 $self->{set_next_char}->($self);
3516 wakaba 1.18 }
3517    
3518    
3519     return ($self->{current_token}); # DOCTYPE
3520    
3521     redo A;
3522 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3523 wakaba 1.77
3524 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed DOCTYPE');
3525 wakaba 1.18
3526 wakaba 1.59 $self->{state} = DATA_STATE;
3527 wakaba 1.26 ## reconsume
3528 wakaba 1.18
3529 wakaba 1.75 $self->{current_token}->{quirks} = 1;
3530 wakaba 1.18 return ($self->{current_token}); # DOCTYPE
3531    
3532     redo A;
3533     } else {
3534 wakaba 1.77
3535 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'string after SYSTEM literal');
3536 wakaba 1.75 #$self->{current_token}->{quirks} = 1;
3537 wakaba 1.73
3538 wakaba 1.59 $self->{state} = BOGUS_DOCTYPE_STATE;
3539 wakaba 1.1
3540     if (@{$self->{char}}) {
3541 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3542 wakaba 1.1 } else {
3543 wakaba 1.76 $self->{set_next_char}->($self);
3544 wakaba 1.1 }
3545    
3546     redo A;
3547     }
3548 wakaba 1.59 } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
3549 wakaba 1.76 if ($self->{next_char} == 0x003E) { # >
3550 wakaba 1.77
3551 wakaba 1.59 $self->{state} = DATA_STATE;
3552 wakaba 1.1
3553     if (@{$self->{char}}) {
3554 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3555 wakaba 1.1 } else {
3556 wakaba 1.76 $self->{set_next_char}->($self);
3557 wakaba 1.1 }
3558    
3559    
3560     return ($self->{current_token}); # DOCTYPE
3561    
3562     redo A;
3563 wakaba 1.76 } elsif ($self->{next_char} == -1) {
3564 wakaba 1.77
3565 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unclosed DOCTYPE');
3566 wakaba 1.59 $self->{state} = DATA_STATE;
3567 wakaba 1.1 ## reconsume
3568    
3569     return ($self->{current_token}); # DOCTYPE
3570    
3571     redo A;
3572     } else {
3573 wakaba 1.77
3574 wakaba 1.1 ## Stay in the state
3575    
3576     if (@{$self->{char}}) {
3577 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3578 wakaba 1.1 } else {
3579 wakaba 1.76 $self->{set_next_char}->($self);
3580 wakaba 1.1 }
3581    
3582     redo A;
3583     }
3584 wakaba 1.124 } elsif ($self->{state} == CDATA_BLOCK_STATE) {
3585     my $s = '';
3586    
3587     my ($l, $c) = ($self->{line}, $self->{column});
3588    
3589     CS: while ($self->{next_char} != -1) {
3590     if ($self->{next_char} == 0x005D) { # ]
3591    
3592     if (@{$self->{char}}) {
3593     $self->{next_char} = shift @{$self->{char}};
3594     } else {
3595     $self->{set_next_char}->($self);
3596     }
3597    
3598     if ($self->{next_char} == 0x005D) { # ]
3599    
3600     if (@{$self->{char}}) {
3601     $self->{next_char} = shift @{$self->{char}};
3602     } else {
3603     $self->{set_next_char}->($self);
3604     }
3605    
3606     MDC: {
3607     if ($self->{next_char} == 0x003E) { # >
3608    
3609    
3610     if (@{$self->{char}}) {
3611     $self->{next_char} = shift @{$self->{char}};
3612     } else {
3613     $self->{set_next_char}->($self);
3614     }
3615    
3616     last CS;
3617     } elsif ($self->{next_char} == 0x005D) { # ]
3618    
3619     $s .= ']';
3620    
3621     if (@{$self->{char}}) {
3622     $self->{next_char} = shift @{$self->{char}};
3623     } else {
3624     $self->{set_next_char}->($self);
3625     }
3626    
3627     redo MDC;
3628     } else {
3629    
3630     $s .= ']]';
3631     #
3632     }
3633     } # MDC
3634     } else {
3635    
3636     $s .= ']';
3637     #
3638     }
3639     } else {
3640    
3641     #
3642     }
3643     $s .= chr $self->{next_char};
3644    
3645     if (@{$self->{char}}) {
3646     $self->{next_char} = shift @{$self->{char}};
3647     } else {
3648     $self->{set_next_char}->($self);
3649     }
3650    
3651     } # CS
3652    
3653     $self->{state} = DATA_STATE;
3654     ## next-input-character done or EOF, which is reconsumed.
3655    
3656     if (length $s) {
3657    
3658     return ({type => CHARACTER_TOKEN, data => $s,
3659     line => $l, column => $c});
3660     } else {
3661    
3662     }
3663    
3664     redo A;
3665    
3666     ## ISSUE: "text tokens" in spec.
3667     ## TODO: Streaming support
3668 wakaba 1.1 } else {
3669     die "$0: $self->{state}: Unknown state";
3670     }
3671     } # A
3672    
3673     die "$0: _get_next_token: unexpected case";
3674     } # _get_next_token
3675    
3676 wakaba 1.72 sub _tokenize_attempt_to_consume_an_entity ($$$) {
3677     my ($self, $in_attr, $additional) = @_;
3678 wakaba 1.20
3679 wakaba 1.110 my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
3680    
3681 wakaba 1.20 if ({
3682     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
3683     0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR
3684 wakaba 1.72 $additional => 1,
3685 wakaba 1.76 }->{$self->{next_char}}) {
3686 wakaba 1.78
3687 wakaba 1.20 ## Don't consume
3688     ## No error
3689     return undef;
3690 wakaba 1.76 } elsif ($self->{next_char} == 0x0023) { # #
3691 wakaba 1.1
3692     if (@{$self->{char}}) {
3693 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3694 wakaba 1.1 } else {
3695 wakaba 1.76 $self->{set_next_char}->($self);
3696 wakaba 1.1 }
3697    
3698 wakaba 1.76 if ($self->{next_char} == 0x0078 or # x
3699     $self->{next_char} == 0x0058) { # X
3700 wakaba 1.26 my $code;
3701 wakaba 1.1 X: {
3702 wakaba 1.76 my $x_char = $self->{next_char};
3703 wakaba 1.1
3704     if (@{$self->{char}}) {
3705 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3706 wakaba 1.1 } else {
3707 wakaba 1.76 $self->{set_next_char}->($self);
3708 wakaba 1.1 }
3709    
3710 wakaba 1.76 if (0x0030 <= $self->{next_char} and
3711     $self->{next_char} <= 0x0039) { # 0..9
3712 wakaba 1.78
3713 wakaba 1.26 $code ||= 0;
3714     $code *= 0x10;
3715 wakaba 1.76 $code += $self->{next_char} - 0x0030;
3716 wakaba 1.1 redo X;
3717 wakaba 1.76 } elsif (0x0061 <= $self->{next_char} and
3718     $self->{next_char} <= 0x0066) { # a..f
3719 wakaba 1.78
3720 wakaba 1.26 $code ||= 0;
3721     $code *= 0x10;
3722 wakaba 1.76 $code += $self->{next_char} - 0x0060 + 9;
3723 wakaba 1.1 redo X;
3724 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
3725     $self->{next_char} <= 0x0046) { # A..F
3726 wakaba 1.78
3727 wakaba 1.26 $code ||= 0;
3728     $code *= 0x10;
3729 wakaba 1.76 $code += $self->{next_char} - 0x0040 + 9;
3730 wakaba 1.1 redo X;
3731 wakaba 1.26 } elsif (not defined $code) { # no hexadecimal digit
3732 wakaba 1.78
3733 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'bare hcro', line => $l, column => $c);
3734 wakaba 1.76 unshift @{$self->{char}}, ($x_char, $self->{next_char});
3735     $self->{next_char} = 0x0023; # #
3736 wakaba 1.1 return undef;
3737 wakaba 1.76 } elsif ($self->{next_char} == 0x003B) { # ;
3738 wakaba 1.1
3739 wakaba 1.78
3740 wakaba 1.1 if (@{$self->{char}}) {
3741 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3742 wakaba 1.1 } else {
3743 wakaba 1.76 $self->{set_next_char}->($self);
3744 wakaba 1.1 }
3745    
3746     } else {
3747 wakaba 1.78
3748 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'no refc', line => $l, column => $c);
3749 wakaba 1.1 }
3750    
3751 wakaba 1.26 if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
3752 wakaba 1.78
3753 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
3754 wakaba 1.26 $code = 0xFFFD;
3755     } elsif ($code > 0x10FFFF) {
3756 wakaba 1.78
3757 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
3758 wakaba 1.26 $code = 0xFFFD;
3759     } elsif ($code == 0x000D) {
3760 wakaba 1.78
3761 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'CR character reference', line => $l, column => $c);
3762 wakaba 1.26 $code = 0x000A;
3763     } elsif (0x80 <= $code and $code <= 0x9F) {
3764 wakaba 1.78
3765 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
3766 wakaba 1.26 $code = $c1_entity_char->{$code};
3767 wakaba 1.1 }
3768    
3769 wakaba 1.68 return {type => CHARACTER_TOKEN, data => chr $code,
3770 wakaba 1.116 has_reference => 1,
3771 wakaba 1.118 line => $l, column => $c,
3772 wakaba 1.116 };
3773 wakaba 1.1 } # X
3774 wakaba 1.76 } elsif (0x0030 <= $self->{next_char} and
3775     $self->{next_char} <= 0x0039) { # 0..9
3776     my $code = $self->{next_char} - 0x0030;
3777 wakaba 1.1
3778     if (@{$self->{char}}) {
3779 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3780 wakaba 1.1 } else {
3781 wakaba 1.76 $self->{set_next_char}->($self);
3782 wakaba 1.1 }
3783    
3784    
3785 wakaba 1.76 while (0x0030 <= $self->{next_char} and
3786     $self->{next_char} <= 0x0039) { # 0..9
3787 wakaba 1.78
3788 wakaba 1.1 $code *= 10;
3789 wakaba 1.76 $code += $self->{next_char} - 0x0030;
3790 wakaba 1.1
3791    
3792     if (@{$self->{char}}) {
3793 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3794 wakaba 1.1 } else {
3795 wakaba 1.76 $self->{set_next_char}->($self);
3796 wakaba 1.1 }
3797    
3798     }
3799    
3800 wakaba 1.76 if ($self->{next_char} == 0x003B) { # ;
3801 wakaba 1.1
3802 wakaba 1.78
3803 wakaba 1.1 if (@{$self->{char}}) {
3804 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3805 wakaba 1.1 } else {
3806 wakaba 1.76 $self->{set_next_char}->($self);
3807 wakaba 1.1 }
3808    
3809     } else {
3810 wakaba 1.78
3811 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'no refc', line => $l, column => $c);
3812 wakaba 1.1 }
3813    
3814 wakaba 1.26 if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
3815 wakaba 1.78
3816 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
3817 wakaba 1.26 $code = 0xFFFD;
3818     } elsif ($code > 0x10FFFF) {
3819 wakaba 1.78
3820 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
3821 wakaba 1.26 $code = 0xFFFD;
3822     } elsif ($code == 0x000D) {
3823 wakaba 1.78
3824 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'CR character reference', line => $l, column => $c);
3825 wakaba 1.26 $code = 0x000A;
3826 wakaba 1.4 } elsif (0x80 <= $code and $code <= 0x9F) {
3827 wakaba 1.78
3828 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
3829 wakaba 1.4 $code = $c1_entity_char->{$code};
3830 wakaba 1.1 }
3831    
3832 wakaba 1.110 return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,
3833 wakaba 1.118 line => $l, column => $c,
3834 wakaba 1.116 };
3835 wakaba 1.1 } else {
3836 wakaba 1.78
3837 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'bare nero', line => $l, column => $c);
3838 wakaba 1.76 unshift @{$self->{char}}, ($self->{next_char});
3839     $self->{next_char} = 0x0023; # #
3840 wakaba 1.1 return undef;
3841     }
3842 wakaba 1.76 } elsif ((0x0041 <= $self->{next_char} and
3843     $self->{next_char} <= 0x005A) or
3844     (0x0061 <= $self->{next_char} and
3845     $self->{next_char} <= 0x007A)) {
3846     my $entity_name = chr $self->{next_char};
3847 wakaba 1.1
3848     if (@{$self->{char}}) {
3849 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3850 wakaba 1.1 } else {
3851 wakaba 1.76 $self->{set_next_char}->($self);
3852 wakaba 1.1 }
3853    
3854    
3855     my $value = $entity_name;
3856 wakaba 1.37 my $match = 0;
3857 wakaba 1.16 require Whatpm::_NamedEntityList;
3858     our $EntityChar;
3859 wakaba 1.1
3860 wakaba 1.125 while (length $entity_name < 30 and
3861 wakaba 1.1 ## NOTE: Some number greater than the maximum length of entity name
3862 wakaba 1.76 ((0x0041 <= $self->{next_char} and # a
3863     $self->{next_char} <= 0x005A) or # x
3864     (0x0061 <= $self->{next_char} and # a
3865     $self->{next_char} <= 0x007A) or # z
3866     (0x0030 <= $self->{next_char} and # 0
3867     $self->{next_char} <= 0x0039) or # 9
3868     $self->{next_char} == 0x003B)) { # ;
3869     $entity_name .= chr $self->{next_char};
3870 wakaba 1.16 if (defined $EntityChar->{$entity_name}) {
3871 wakaba 1.76 if ($self->{next_char} == 0x003B) { # ;
3872 wakaba 1.78
3873 wakaba 1.26 $value = $EntityChar->{$entity_name};
3874 wakaba 1.16 $match = 1;
3875    
3876     if (@{$self->{char}}) {
3877 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3878 wakaba 1.16 } else {
3879 wakaba 1.76 $self->{set_next_char}->($self);
3880 wakaba 1.16 }
3881    
3882     last;
3883 wakaba 1.37 } else {
3884 wakaba 1.78
3885 wakaba 1.26 $value = $EntityChar->{$entity_name};
3886     $match = -1;
3887 wakaba 1.37
3888     if (@{$self->{char}}) {
3889 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3890 wakaba 1.37 } else {
3891 wakaba 1.76 $self->{set_next_char}->($self);
3892 wakaba 1.37 }
3893    
3894 wakaba 1.16 }
3895 wakaba 1.1 } else {
3896 wakaba 1.78
3897 wakaba 1.76 $value .= chr $self->{next_char};
3898 wakaba 1.37 $match *= 2;
3899    
3900 wakaba 1.1 if (@{$self->{char}}) {
3901 wakaba 1.76 $self->{next_char} = shift @{$self->{char}};
3902 wakaba 1.1 } else {
3903 wakaba 1.76 $self->{set_next_char}->($self);
3904 wakaba 1.1 }
3905    
3906 wakaba 1.37 }
3907 wakaba 1.1 }
3908    
3909 wakaba 1.16 if ($match > 0) {
3910 wakaba 1.78
3911 wakaba 1.110 return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
3912 wakaba 1.118 line => $l, column => $c,
3913 wakaba 1.116 };
3914 wakaba 1.16 } elsif ($match < 0) {
3915 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'no refc', line => $l, column => $c);
3916 wakaba 1.37 if ($in_attr and $match < -1) {
3917 wakaba 1.78
3918 wakaba 1.110 return {type => CHARACTER_TOKEN, data => '&'.$entity_name,
3919 wakaba 1.118 line => $l, column => $c,
3920 wakaba 1.116 };
3921 wakaba 1.37 } else {
3922 wakaba 1.78
3923 wakaba 1.110 return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
3924 wakaba 1.118 line => $l, column => $c,
3925 wakaba 1.116 };
3926 wakaba 1.37 }
3927 wakaba 1.1 } else {
3928 wakaba 1.78
3929 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'bare ero', line => $l, column => $c);
3930 wakaba 1.68 ## NOTE: "No characters are consumed" in the spec.
3931 wakaba 1.110 return {type => CHARACTER_TOKEN, data => '&'.$value,
3932 wakaba 1.118 line => $l, column => $c,
3933 wakaba 1.116 };
3934 wakaba 1.1 }
3935     } else {
3936 wakaba 1.78
3937 wakaba 1.1 ## no characters are consumed
3938 wakaba 1.110 $self->{parse_error}->(level => $self->{must_level}, type => 'bare ero', line => $l, column => $c);
3939 wakaba 1.1 return undef;
3940     }
3941     } # _tokenize_attempt_to_consume_an_entity
3942    
3943     sub _initialize_tree_constructor ($) {
3944     my $self = shift;
3945     ## NOTE: $self->{document} MUST be specified before this method is called
3946     $self->{document}->strict_error_checking (0);
3947     ## TODO: Turn mutation events off # MUST
3948     ## TODO: Turn loose Document option (manakai extension) on
3949 wakaba 1.18 $self->{document}->manakai_is_html (1); # MUST
3950 wakaba 1.1 } # _initialize_tree_constructor
3951    
3952     sub _terminate_tree_constructor ($) {
3953     my $self = shift;
3954     $self->{document}->strict_error_checking (1);
3955     ## TODO: Turn mutation events on
3956     } # _terminate_tree_constructor
3957    
3958     ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?
3959    
3960 wakaba 1.3 { # tree construction stage
3961     my $token;
3962    
3963 wakaba 1.1 sub _construct_tree ($) {
3964     my ($self) = @_;
3965    
3966     ## When an interactive UA render the $self->{document} available
3967     ## to the user, or when it begin accepting user input, are
3968     ## not defined.
3969    
3970     ## Append a character: collect it and all subsequent consecutive
3971     ## characters and insert one Text node whose data is concatenation
3972     ## of all those characters. # MUST
3973    
3974     $token = $self->_get_next_token;
3975    
3976 wakaba 1.3 undef $self->{form_element};
3977     undef $self->{head_element};
3978     $self->{open_elements} = [];
3979     undef $self->{inner_html_node};
3980    
3981 wakaba 1.84 ## NOTE: The "initial" insertion mode.
3982 wakaba 1.3 $self->_tree_construction_initial; # MUST
3983 wakaba 1.84
3984 wakaba 1.85 ## NOTE: The "before html" insertion mode.
3985 wakaba 1.3 $self->_tree_construction_root_element;
3986 wakaba 1.84 $self->{insertion_mode} = BEFORE_HEAD_IM;
3987    
3988     ## NOTE: The "before head" insertion mode and so on.
3989 wakaba 1.3 $self->_tree_construction_main;
3990     } # _construct_tree
3991    
3992     sub _tree_construction_initial ($) {
3993     my $self = shift;
3994 wakaba 1.84
3995     ## NOTE: "initial" insertion mode
3996    
3997 wakaba 1.18 INITIAL: {
3998 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
3999 wakaba 1.18 ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
4000     ## error, switch to a conformance checking mode for another
4001     ## language.
4002     my $doctype_name = $token->{name};
4003     $doctype_name = '' unless defined $doctype_name;
4004     $doctype_name =~ tr/a-z/A-Z/;
4005     if (not defined $token->{name} or # <!DOCTYPE>
4006     defined $token->{public_identifier} or
4007     defined $token->{system_identifier}) {
4008 wakaba 1.79
4009 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'not HTML5', token => $token);
4010 wakaba 1.18 } elsif ($doctype_name ne 'HTML') {
4011 wakaba 1.79
4012 wakaba 1.18 ## ISSUE: ASCII case-insensitive? (in fact it does not matter)
4013 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'not HTML5', token => $token);
4014 wakaba 1.79 } else {
4015    
4016 wakaba 1.18 }
4017    
4018     my $doctype = $self->{document}->create_document_type_definition
4019     ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
4020 wakaba 1.120 ## NOTE: Default value for both |public_id| and |system_id| attributes
4021     ## are empty strings, so that we don't set any value in missing cases.
4022 wakaba 1.18 $doctype->public_id ($token->{public_identifier})
4023     if defined $token->{public_identifier};
4024     $doctype->system_id ($token->{system_identifier})
4025     if defined $token->{system_identifier};
4026     ## NOTE: Other DocumentType attributes are null or empty lists.
4027     ## ISSUE: internalSubset = null??
4028     $self->{document}->append_child ($doctype);
4029    
4030 wakaba 1.75 if ($token->{quirks} or $doctype_name ne 'HTML') {
4031 wakaba 1.79
4032 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
4033     } elsif (defined $token->{public_identifier}) {
4034     my $pubid = $token->{public_identifier};
4035     $pubid =~ tr/a-z/A-z/;
4036     if ({
4037     "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,
4038     "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,
4039     "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,
4040     "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,
4041     "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,
4042     "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,
4043     "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,
4044     "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,
4045     "-//IETF//DTD HTML 2.0//EN" => 1,
4046     "-//IETF//DTD HTML 2.1E//EN" => 1,
4047     "-//IETF//DTD HTML 3.0//EN" => 1,
4048     "-//IETF//DTD HTML 3.0//EN//" => 1,
4049     "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,
4050     "-//IETF//DTD HTML 3.2//EN" => 1,
4051     "-//IETF//DTD HTML 3//EN" => 1,
4052     "-//IETF//DTD HTML LEVEL 0//EN" => 1,
4053     "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,
4054     "-//IETF//DTD HTML LEVEL 1//EN" => 1,
4055     "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,
4056     "-//IETF//DTD HTML LEVEL 2//EN" => 1,
4057     "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,
4058     "-//IETF//DTD HTML LEVEL 3//EN" => 1,
4059     "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,
4060     "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,
4061     "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,
4062     "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,
4063     "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,
4064     "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,
4065     "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,
4066     "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,
4067     "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,
4068     "-//IETF//DTD HTML STRICT//EN" => 1,
4069     "-//IETF//DTD HTML STRICT//EN//2.0" => 1,
4070     "-//IETF//DTD HTML STRICT//EN//3.0" => 1,
4071     "-//IETF//DTD HTML//EN" => 1,
4072     "-//IETF//DTD HTML//EN//2.0" => 1,
4073     "-//IETF//DTD HTML//EN//3.0" => 1,
4074     "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,
4075     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,
4076     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,
4077     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,
4078     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,
4079     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,
4080     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,
4081     "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,
4082     "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,
4083     "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,
4084     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,
4085 wakaba 1.72 "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,
4086     "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,
4087     "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,
4088 wakaba 1.18 "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,
4089     "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,
4090     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,
4091     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,
4092     "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,
4093     "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,
4094     "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,
4095     "-//W3C//DTD HTML 3.2//EN" => 1,
4096     "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,
4097     "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,
4098     "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,
4099     "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,
4100     "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,
4101     "-//W3C//DTD W3 HTML//EN" => 1,
4102     "-//W3O//DTD W3 HTML 3.0//EN" => 1,
4103     "-//W3O//DTD W3 HTML 3.0//EN//" => 1,
4104     "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,
4105     "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,
4106     "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,
4107     "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,
4108     "HTML" => 1,
4109     }->{$pubid}) {
4110 wakaba 1.79
4111 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
4112     } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or
4113     $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {
4114     if (defined $token->{system_identifier}) {
4115 wakaba 1.79
4116 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
4117     } else {
4118 wakaba 1.79
4119 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
4120 wakaba 1.3 }
4121 wakaba 1.80 } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or
4122     $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {
4123 wakaba 1.79
4124 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
4125 wakaba 1.79 } else {
4126    
4127 wakaba 1.18 }
4128 wakaba 1.79 } else {
4129    
4130 wakaba 1.18 }
4131     if (defined $token->{system_identifier}) {
4132     my $sysid = $token->{system_identifier};
4133     $sysid =~ tr/A-Z/a-z/;
4134     if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
4135 wakaba 1.80 ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"
4136 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
4137 wakaba 1.79
4138     } else {
4139    
4140 wakaba 1.18 }
4141 wakaba 1.79 } else {
4142    
4143 wakaba 1.18 }
4144    
4145 wakaba 1.85 ## Go to the "before html" insertion mode.
4146 wakaba 1.18 $token = $self->_get_next_token;
4147     return;
4148     } elsif ({
4149 wakaba 1.57 START_TAG_TOKEN, 1,
4150     END_TAG_TOKEN, 1,
4151     END_OF_FILE_TOKEN, 1,
4152 wakaba 1.18 }->{$token->{type}}) {
4153 wakaba 1.79
4154 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'no DOCTYPE', token => $token);
4155 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
4156 wakaba 1.85 ## Go to the "before html" insertion mode.
4157 wakaba 1.18 ## reprocess
4158 wakaba 1.122
4159 wakaba 1.18 return;
4160 wakaba 1.57 } elsif ($token->{type} == CHARACTER_TOKEN) {
4161 wakaba 1.18 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
4162     ## Ignore the token
4163 wakaba 1.26
4164 wakaba 1.18 unless (length $token->{data}) {
4165 wakaba 1.79
4166 wakaba 1.84 ## Stay in the insertion mode.
4167 wakaba 1.18 $token = $self->_get_next_token;
4168     redo INITIAL;
4169 wakaba 1.79 } else {
4170    
4171 wakaba 1.3 }
4172 wakaba 1.79 } else {
4173    
4174 wakaba 1.3 }
4175 wakaba 1.18
4176 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'no DOCTYPE', token => $token);
4177 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
4178 wakaba 1.85 ## Go to the "before html" insertion mode.
4179 wakaba 1.18 ## reprocess
4180     return;
4181 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
4182 wakaba 1.79
4183 wakaba 1.18 my $comment = $self->{document}->create_comment ($token->{data});
4184     $self->{document}->append_child ($comment);
4185    
4186 wakaba 1.84 ## Stay in the insertion mode.
4187 wakaba 1.18 $token = $self->_get_next_token;
4188     redo INITIAL;
4189     } else {
4190 wakaba 1.57 die "$0: $token->{type}: Unknown token type";
4191 wakaba 1.18 }
4192     } # INITIAL
4193 wakaba 1.79
4194     die "$0: _tree_construction_initial: This should be never reached";
4195 wakaba 1.3 } # _tree_construction_initial
4196    
4197     sub _tree_construction_root_element ($) {
4198     my $self = shift;
4199 wakaba 1.84
4200 wakaba 1.85 ## NOTE: "before html" insertion mode.
4201 wakaba 1.3
4202     B: {
4203 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
4204 wakaba 1.79
4205 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in html:#DOCTYPE', token => $token);
4206 wakaba 1.3 ## Ignore the token
4207 wakaba 1.84 ## Stay in the insertion mode.
4208 wakaba 1.3 $token = $self->_get_next_token;
4209     redo B;
4210 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
4211 wakaba 1.79
4212 wakaba 1.3 my $comment = $self->{document}->create_comment ($token->{data});
4213     $self->{document}->append_child ($comment);
4214 wakaba 1.84 ## Stay in the insertion mode.
4215 wakaba 1.3 $token = $self->_get_next_token;
4216     redo B;
4217 wakaba 1.57 } elsif ($token->{type} == CHARACTER_TOKEN) {
4218 wakaba 1.26 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
4219     ## Ignore the token.
4220    
4221 wakaba 1.3 unless (length $token->{data}) {
4222 wakaba 1.79
4223 wakaba 1.84 ## Stay in the insertion mode.
4224 wakaba 1.3 $token = $self->_get_next_token;
4225     redo B;
4226 wakaba 1.79 } else {
4227    
4228 wakaba 1.3 }
4229 wakaba 1.79 } else {
4230    
4231 wakaba 1.3 }
4232 wakaba 1.63
4233     $self->{application_cache_selection}->(undef);
4234    
4235     #
4236     } elsif ($token->{type} == START_TAG_TOKEN) {
4237 wakaba 1.84 if ($token->{tag_name} eq 'html') {
4238     my $root_element;
4239 wakaba 1.79
4240 wakaba 1.84 $root_element = $self->{document}->create_element_ns
4241 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
4242 wakaba 1.84
4243     for my $attr_name (keys %{ $token->{attributes}}) {
4244 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
4245 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
4246 wakaba 1.117 $attr->value ($attr_t->{value});
4247     $attr->set_user_data (manakai_source_line => $attr_t->{line});
4248     $attr->set_user_data (manakai_source_column => $attr_t->{column});
4249     $root_element->set_attribute_node_ns ($attr);
4250 wakaba 1.84 }
4251    
4252 wakaba 1.114 $root_element->set_user_data (manakai_source_line => $token->{line})
4253     if defined $token->{line};
4254     $root_element->set_user_data (manakai_source_column => $token->{column})
4255     if defined $token->{column};
4256    
4257 wakaba 1.84 $self->{document}->append_child ($root_element);
4258 wakaba 1.121 push @{$self->{open_elements}},
4259     [$root_element, $el_category->{html}];
4260 wakaba 1.84
4261     if ($token->{attributes}->{manifest}) {
4262    
4263     $self->{application_cache_selection}
4264     ->($token->{attributes}->{manifest}->{value});
4265 wakaba 1.116 ## ISSUE: Spec is unclear on relative references.
4266     ## According to Hixie (#whatwg 2008-03-19), it should be
4267     ## resolved against the base URI of the document in HTML
4268     ## or xml:base of the element in XHTML.
4269 wakaba 1.84 } else {
4270    
4271     $self->{application_cache_selection}->(undef);
4272     }
4273    
4274 wakaba 1.122
4275    
4276 wakaba 1.84 $token = $self->_get_next_token;
4277     return; ## Go to the "before head" insertion mode.
4278 wakaba 1.63 } else {
4279 wakaba 1.79
4280 wakaba 1.84 #
4281 wakaba 1.63 }
4282 wakaba 1.3 } elsif ({
4283 wakaba 1.57 END_TAG_TOKEN, 1,
4284     END_OF_FILE_TOKEN, 1,
4285 wakaba 1.3 }->{$token->{type}}) {
4286 wakaba 1.79
4287 wakaba 1.3 #
4288     } else {
4289 wakaba 1.57 die "$0: $token->{type}: Unknown token type";
4290 wakaba 1.3 }
4291 wakaba 1.63
4292 wakaba 1.123 my $root_element;
4293    
4294 wakaba 1.3 $root_element = $self->{document}->create_element_ns
4295 wakaba 1.128 ($HTML_NS, [undef, 'html']);
4296 wakaba 1.3
4297 wakaba 1.114 $root_element->set_user_data (manakai_source_line => $token->{line})
4298     if defined $token->{line};
4299     $root_element->set_user_data (manakai_source_column => $token->{column})
4300     if defined $token->{column};
4301    
4302 wakaba 1.84 $self->{document}->append_child ($root_element);
4303 wakaba 1.121 push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
4304 wakaba 1.84
4305     $self->{application_cache_selection}->(undef);
4306    
4307     ## NOTE: Reprocess the token.
4308 wakaba 1.122
4309 wakaba 1.84 return; ## Go to the "before head" insertion mode.
4310    
4311     ## ISSUE: There is an issue in the spec
4312 wakaba 1.3 } # B
4313 wakaba 1.79
4314     die "$0: _tree_construction_root_element: This should never be reached";
4315 wakaba 1.3 } # _tree_construction_root_element
4316    
4317     sub _reset_insertion_mode ($) {
4318     my $self = shift;
4319    
4320     ## Step 1
4321     my $last;
4322    
4323     ## Step 2
4324     my $i = -1;
4325     my $node = $self->{open_elements}->[$i];
4326    
4327     ## Step 3
4328     S3: {
4329 wakaba 1.29 if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
4330     $last = 1;
4331     if (defined $self->{inner_html_node}) {
4332 wakaba 1.137
4333     $node = $self->{inner_html_node};
4334     } else {
4335     die "_reset_insertion_mode: t27";
4336 wakaba 1.3 }
4337     }
4338 wakaba 1.137
4339     ## Step 4..14
4340     my $new_mode;
4341     if ($node->[1] & FOREIGN_EL) {
4342    
4343     ## NOTE: Strictly spaking, the line below only applies to MathML and
4344     ## SVG elements. Currently the HTML syntax supports only MathML and
4345     ## SVG elements as foreigners.
4346     $new_mode = $self->{insertion_mode} | IN_FOREIGN_CONTENT_IM;
4347     ## ISSUE: What is set as the secondary insertion mode?
4348     } elsif ($node->[1] & TABLE_CELL_EL) {
4349     if ($last) {
4350    
4351     #
4352     } else {
4353    
4354     $new_mode = IN_CELL_IM;
4355     }
4356     } else {
4357    
4358     $new_mode = {
4359 wakaba 1.56 select => IN_SELECT_IM,
4360 wakaba 1.83 ## NOTE: |option| and |optgroup| do not set
4361     ## insertion mode to "in select" by themselves.
4362 wakaba 1.56 tr => IN_ROW_IM,
4363     tbody => IN_TABLE_BODY_IM,
4364     thead => IN_TABLE_BODY_IM,
4365     tfoot => IN_TABLE_BODY_IM,
4366     caption => IN_CAPTION_IM,
4367     colgroup => IN_COLUMN_GROUP_IM,
4368     table => IN_TABLE_IM,
4369     head => IN_BODY_IM, # not in head!
4370     body => IN_BODY_IM,
4371     frameset => IN_FRAMESET_IM,
4372 wakaba 1.121 }->{$node->[0]->manakai_local_name};
4373 wakaba 1.137 }
4374     $self->{insertion_mode} = $new_mode and return if defined $new_mode;
4375 wakaba 1.3
4376 wakaba 1.123 ## Step 15
4377 wakaba 1.121 if ($node->[1] & HTML_EL) {
4378 wakaba 1.3 unless (defined $self->{head_element}) {
4379 wakaba 1.79
4380 wakaba 1.56 $self->{insertion_mode} = BEFORE_HEAD_IM;
4381 wakaba 1.3 } else {
4382 wakaba 1.81 ## ISSUE: Can this state be reached?
4383 wakaba 1.79
4384 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
4385 wakaba 1.3 }
4386     return;
4387 wakaba 1.79 } else {
4388    
4389 wakaba 1.3 }
4390    
4391 wakaba 1.123 ## Step 16
4392 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM and return if $last;
4393 wakaba 1.3
4394 wakaba 1.123 ## Step 17
4395 wakaba 1.3 $i--;
4396     $node = $self->{open_elements}->[$i];
4397    
4398 wakaba 1.123 ## Step 18
4399 wakaba 1.3 redo S3;
4400     } # S3
4401 wakaba 1.79
4402     die "$0: _reset_insertion_mode: This line should never be reached";
4403 wakaba 1.3 } # _reset_insertion_mode
4404    
4405     sub _tree_construction_main ($) {
4406     my $self = shift;
4407    
4408 wakaba 1.1 my $active_formatting_elements = [];
4409    
4410     my $reconstruct_active_formatting_elements = sub { # MUST
4411     my $insert = shift;
4412    
4413     ## Step 1
4414     return unless @$active_formatting_elements;
4415    
4416     ## Step 3
4417     my $i = -1;
4418     my $entry = $active_formatting_elements->[$i];
4419    
4420     ## Step 2
4421     return if $entry->[0] eq '#marker';
4422 wakaba 1.3 for (@{$self->{open_elements}}) {
4423 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
4424 wakaba 1.79
4425 wakaba 1.1 return;
4426     }
4427     }
4428    
4429     S4: {
4430     ## Step 4
4431     last S4 if $active_formatting_elements->[0]->[0] eq $entry->[0];
4432    
4433     ## Step 5
4434     $i--;
4435     $entry = $active_formatting_elements->[$i];
4436    
4437     ## Step 6
4438     if ($entry->[0] eq '#marker') {
4439 wakaba 1.79
4440 wakaba 1.1 #
4441     } else {
4442     my $in_open_elements;
4443 wakaba 1.3 OE: for (@{$self->{open_elements}}) {
4444 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
4445 wakaba 1.79
4446 wakaba 1.1 $in_open_elements = 1;
4447     last OE;
4448     }
4449     }
4450     if ($in_open_elements) {
4451 wakaba 1.79
4452 wakaba 1.1 #
4453     } else {
4454 wakaba 1.81 ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
4455 wakaba 1.79
4456 wakaba 1.1 redo S4;
4457     }
4458     }
4459    
4460     ## Step 7
4461     $i++;
4462     $entry = $active_formatting_elements->[$i];
4463     } # S4
4464    
4465     S7: {
4466     ## Step 8
4467     my $clone = [$entry->[0]->clone_node (0), $entry->[1]];
4468    
4469     ## Step 9
4470     $insert->($clone->[0]);
4471 wakaba 1.3 push @{$self->{open_elements}}, $clone;
4472 wakaba 1.1
4473     ## Step 10
4474 wakaba 1.3 $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
4475 wakaba 1.1
4476     ## Step 11
4477     unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
4478 wakaba 1.79
4479 wakaba 1.1 ## Step 7'
4480     $i++;
4481     $entry = $active_formatting_elements->[$i];
4482    
4483     redo S7;
4484     }
4485 wakaba 1.79
4486    
4487 wakaba 1.1 } # S7
4488     }; # $reconstruct_active_formatting_elements
4489    
4490     my $clear_up_to_marker = sub {
4491     for (reverse 0..$#$active_formatting_elements) {
4492     if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4493 wakaba 1.79
4494 wakaba 1.1 splice @$active_formatting_elements, $_;
4495     return;
4496     }
4497     }
4498 wakaba 1.79
4499    
4500 wakaba 1.1 }; # $clear_up_to_marker
4501    
4502 wakaba 1.96 my $insert;
4503    
4504     my $parse_rcdata = sub ($) {
4505     my ($content_model_flag) = @_;
4506 wakaba 1.25
4507     ## Step 1
4508     my $start_tag_name = $token->{tag_name};
4509     my $el;
4510    
4511     $el = $self->{document}->create_element_ns
4512 wakaba 1.128 ($HTML_NS, [undef, $start_tag_name]);
4513 wakaba 1.1
4514 wakaba 1.6 for my $attr_name (keys %{ $token->{attributes}}) {
4515 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
4516 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
4517 wakaba 1.117 $attr->value ($attr_t->{value});
4518     $attr->set_user_data (manakai_source_line => $attr_t->{line});
4519     $attr->set_user_data (manakai_source_column => $attr_t->{column});
4520     $el->set_attribute_node_ns ($attr);
4521 wakaba 1.6 }
4522    
4523 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
4524     if defined $token->{line};
4525     $el->set_user_data (manakai_source_column => $token->{column})
4526     if defined $token->{column};
4527    
4528 wakaba 1.25
4529     ## Step 2
4530 wakaba 1.96 $insert->($el);
4531 wakaba 1.25
4532     ## Step 3
4533 wakaba 1.41 $self->{content_model} = $content_model_flag; # CDATA or RCDATA
4534 wakaba 1.13 delete $self->{escape}; # MUST
4535 wakaba 1.25
4536     ## Step 4
4537 wakaba 1.1 my $text = '';
4538 wakaba 1.122
4539 wakaba 1.1 $token = $self->_get_next_token;
4540 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
4541 wakaba 1.79
4542 wakaba 1.1 $text .= $token->{data};
4543     $token = $self->_get_next_token;
4544 wakaba 1.25 }
4545    
4546     ## Step 5
4547 wakaba 1.1 if (length $text) {
4548 wakaba 1.79
4549 wakaba 1.25 my $text = $self->{document}->create_text_node ($text);
4550     $el->append_child ($text);
4551 wakaba 1.1 }
4552 wakaba 1.25
4553     ## Step 6
4554 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL;
4555 wakaba 1.25
4556     ## Step 7
4557 wakaba 1.79 if ($token->{type} == END_TAG_TOKEN and
4558     $token->{tag_name} eq $start_tag_name) {
4559    
4560 wakaba 1.1 ## Ignore the token
4561     } else {
4562 wakaba 1.96 ## NOTE: An end-of-file token.
4563     if ($content_model_flag == CDATA_CONTENT_MODEL) {
4564    
4565 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in CDATA:#'.$token->{type}, token => $token);
4566 wakaba 1.96 } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4567    
4568 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in RCDATA:#'.$token->{type}, token => $token);
4569 wakaba 1.96 } else {
4570     die "$0: $content_model_flag in parse_rcdata";
4571     }
4572 wakaba 1.1 }
4573     $token = $self->_get_next_token;
4574 wakaba 1.25 }; # $parse_rcdata
4575 wakaba 1.1
4576 wakaba 1.96 my $script_start_tag = sub () {
4577 wakaba 1.1 my $script_el;
4578    
4579     $script_el = $self->{document}->create_element_ns
4580 wakaba 1.128 ($HTML_NS, [undef, 'script']);
4581 wakaba 1.1
4582     for my $attr_name (keys %{ $token->{attributes}}) {
4583 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
4584 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
4585 wakaba 1.117 $attr->value ($attr_t->{value});
4586     $attr->set_user_data (manakai_source_line => $attr_t->{line});
4587     $attr->set_user_data (manakai_source_column => $attr_t->{column});
4588     $script_el->set_attribute_node_ns ($attr);
4589 wakaba 1.1 }
4590    
4591 wakaba 1.114 $script_el->set_user_data (manakai_source_line => $token->{line})
4592     if defined $token->{line};
4593     $script_el->set_user_data (manakai_source_column => $token->{column})
4594     if defined $token->{column};
4595    
4596 wakaba 1.1 ## TODO: mark as "parser-inserted"
4597    
4598 wakaba 1.41 $self->{content_model} = CDATA_CONTENT_MODEL;
4599 wakaba 1.13 delete $self->{escape}; # MUST
4600 wakaba 1.1
4601     my $text = '';
4602 wakaba 1.122
4603 wakaba 1.1 $token = $self->_get_next_token;
4604 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) {
4605 wakaba 1.79
4606 wakaba 1.1 $text .= $token->{data};
4607     $token = $self->_get_next_token;
4608     } # stop if non-character token or tokenizer stops tokenising
4609     if (length $text) {
4610 wakaba 1.79
4611 wakaba 1.1 $script_el->manakai_append_text ($text);
4612     }
4613    
4614 wakaba 1.41 $self->{content_model} = PCDATA_CONTENT_MODEL;
4615 wakaba 1.1
4616 wakaba 1.57 if ($token->{type} == END_TAG_TOKEN and
4617 wakaba 1.1 $token->{tag_name} eq 'script') {
4618 wakaba 1.79
4619 wakaba 1.1 ## Ignore the token
4620     } else {
4621 wakaba 1.79
4622 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in CDATA:#'.$token->{type}, token => $token);
4623 wakaba 1.1 ## ISSUE: And ignore?
4624     ## TODO: mark as "already executed"
4625     }
4626    
4627 wakaba 1.3 if (defined $self->{inner_html_node}) {
4628 wakaba 1.79
4629 wakaba 1.3 ## TODO: mark as "already executed"
4630     } else {
4631 wakaba 1.79
4632 wakaba 1.1 ## TODO: $old_insertion_point = current insertion point
4633     ## TODO: insertion point = just before the next input character
4634 wakaba 1.25
4635     $insert->($script_el);
4636 wakaba 1.1
4637     ## TODO: insertion point = $old_insertion_point (might be "undefined")
4638    
4639     ## TODO: if there is a script that will execute as soon as the parser resume, then...
4640     }
4641    
4642     $token = $self->_get_next_token;
4643     }; # $script_start_tag
4644    
4645 wakaba 1.102 ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4646     ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4647     my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4648    
4649 wakaba 1.1 my $formatting_end_tag = sub {
4650 wakaba 1.111 my $end_tag_token = shift;
4651     my $tag_name = $end_tag_token->{tag_name};
4652 wakaba 1.1
4653 wakaba 1.102 ## NOTE: The adoption agency algorithm (AAA).
4654    
4655 wakaba 1.1 FET: {
4656     ## Step 1
4657     my $formatting_element;
4658     my $formatting_element_i_in_active;
4659     AFE: for (reverse 0..$#$active_formatting_elements) {
4660 wakaba 1.121 if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4661    
4662     last AFE;
4663     } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4664     eq $tag_name) {
4665 wakaba 1.79
4666 wakaba 1.1 $formatting_element = $active_formatting_elements->[$_];
4667     $formatting_element_i_in_active = $_;
4668     last AFE;
4669     }
4670     } # AFE
4671     unless (defined $formatting_element) {
4672 wakaba 1.79
4673 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);
4674 wakaba 1.1 ## Ignore the token
4675     $token = $self->_get_next_token;
4676     return;
4677     }
4678     ## has an element in scope
4679     my $in_scope = 1;
4680     my $formatting_element_i_in_open;
4681 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4682     my $node = $self->{open_elements}->[$_];
4683 wakaba 1.1 if ($node->[0] eq $formatting_element->[0]) {
4684     if ($in_scope) {
4685 wakaba 1.79
4686 wakaba 1.1 $formatting_element_i_in_open = $_;
4687     last INSCOPE;
4688     } else { # in open elements but not in scope
4689 wakaba 1.79
4690 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name},
4691     token => $end_tag_token);
4692 wakaba 1.1 ## Ignore the token
4693     $token = $self->_get_next_token;
4694     return;
4695     }
4696 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
4697 wakaba 1.79
4698 wakaba 1.1 $in_scope = 0;
4699     }
4700     } # INSCOPE
4701     unless (defined $formatting_element_i_in_open) {
4702 wakaba 1.79
4703 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name},
4704     token => $end_tag_token);
4705 wakaba 1.1 pop @$active_formatting_elements; # $formatting_element
4706     $token = $self->_get_next_token; ## TODO: ok?
4707     return;
4708     }
4709 wakaba 1.3 if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4710 wakaba 1.79
4711 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
4712     value => $self->{open_elements}->[-1]->[0]
4713     ->manakai_local_name,
4714 wakaba 1.111 token => $end_tag_token);
4715 wakaba 1.1 }
4716    
4717     ## Step 2
4718     my $furthest_block;
4719     my $furthest_block_i_in_open;
4720 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
4721     my $node = $self->{open_elements}->[$_];
4722 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
4723 wakaba 1.1 #not $phrasing_category->{$node->[1]} and
4724 wakaba 1.121 ($node->[1] & SPECIAL_EL or
4725     $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4726 wakaba 1.79
4727 wakaba 1.1 $furthest_block = $node;
4728     $furthest_block_i_in_open = $_;
4729     } elsif ($node->[0] eq $formatting_element->[0]) {
4730 wakaba 1.79
4731 wakaba 1.1 last OE;
4732     }
4733     } # OE
4734    
4735     ## Step 3
4736     unless (defined $furthest_block) { # MUST
4737 wakaba 1.79
4738 wakaba 1.3 splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4739 wakaba 1.1 splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4740     $token = $self->_get_next_token;
4741     return;
4742     }
4743    
4744     ## Step 4
4745 wakaba 1.3 my $common_ancestor_node = $self->{open_elements}->[$formatting_element_i_in_open - 1];
4746 wakaba 1.1
4747     ## Step 5
4748     my $furthest_block_parent = $furthest_block->[0]->parent_node;
4749     if (defined $furthest_block_parent) {
4750 wakaba 1.79
4751 wakaba 1.1 $furthest_block_parent->remove_child ($furthest_block->[0]);
4752     }
4753    
4754     ## Step 6
4755     my $bookmark_prev_el
4756     = $active_formatting_elements->[$formatting_element_i_in_active - 1]
4757     ->[0];
4758    
4759     ## Step 7
4760     my $node = $furthest_block;
4761     my $node_i_in_open = $furthest_block_i_in_open;
4762     my $last_node = $furthest_block;
4763     S7: {
4764     ## Step 1
4765     $node_i_in_open--;
4766 wakaba 1.3 $node = $self->{open_elements}->[$node_i_in_open];
4767 wakaba 1.1
4768     ## Step 2
4769     my $node_i_in_active;
4770     S7S2: {
4771     for (reverse 0..$#$active_formatting_elements) {
4772     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4773 wakaba 1.79
4774 wakaba 1.1 $node_i_in_active = $_;
4775     last S7S2;
4776     }
4777     }
4778 wakaba 1.3 splice @{$self->{open_elements}}, $node_i_in_open, 1;
4779 wakaba 1.1 redo S7;
4780     } # S7S2
4781    
4782     ## Step 3
4783     last S7 if $node->[0] eq $formatting_element->[0];
4784    
4785     ## Step 4
4786     if ($last_node->[0] eq $furthest_block->[0]) {
4787 wakaba 1.79
4788 wakaba 1.1 $bookmark_prev_el = $node->[0];
4789     }
4790    
4791     ## Step 5
4792     if ($node->[0]->has_child_nodes ()) {
4793 wakaba 1.79
4794 wakaba 1.1 my $clone = [$node->[0]->clone_node (0), $node->[1]];
4795     $active_formatting_elements->[$node_i_in_active] = $clone;
4796 wakaba 1.3 $self->{open_elements}->[$node_i_in_open] = $clone;
4797 wakaba 1.1 $node = $clone;
4798     }
4799    
4800     ## Step 6
4801     $node->[0]->append_child ($last_node->[0]);
4802    
4803     ## Step 7
4804     $last_node = $node;
4805    
4806     ## Step 8
4807     redo S7;
4808     } # S7
4809    
4810     ## Step 8
4811 wakaba 1.121 if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4812 wakaba 1.102 my $foster_parent_element;
4813     my $next_sibling;
4814 wakaba 1.121 OE: for (reverse 0..$#{$self->{open_elements}}) {
4815     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4816 wakaba 1.102 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4817     if (defined $parent and $parent->node_type == 1) {
4818    
4819     $foster_parent_element = $parent;
4820     $next_sibling = $self->{open_elements}->[$_]->[0];
4821     } else {
4822    
4823     $foster_parent_element
4824     = $self->{open_elements}->[$_ - 1]->[0];
4825     }
4826     last OE;
4827     }
4828     } # OE
4829     $foster_parent_element = $self->{open_elements}->[0]->[0]
4830     unless defined $foster_parent_element;
4831     $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4832     $open_tables->[-1]->[1] = 1; # tainted
4833     } else {
4834    
4835     $common_ancestor_node->[0]->append_child ($last_node->[0]);
4836     }
4837 wakaba 1.1
4838     ## Step 9
4839     my $clone = [$formatting_element->[0]->clone_node (0),
4840     $formatting_element->[1]];
4841    
4842     ## Step 10
4843     my @cn = @{$furthest_block->[0]->child_nodes};
4844     $clone->[0]->append_child ($_) for @cn;
4845    
4846     ## Step 11
4847     $furthest_block->[0]->append_child ($clone->[0]);
4848    
4849     ## Step 12
4850     my $i;
4851     AFE: for (reverse 0..$#$active_formatting_elements) {
4852     if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4853 wakaba 1.79
4854 wakaba 1.1 splice @$active_formatting_elements, $_, 1;
4855     $i-- and last AFE if defined $i;
4856     } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4857 wakaba 1.79
4858 wakaba 1.1 $i = $_;
4859     }
4860     } # AFE
4861     splice @$active_formatting_elements, $i + 1, 0, $clone;
4862    
4863     ## Step 13
4864     undef $i;
4865 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
4866     if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4867 wakaba 1.79
4868 wakaba 1.3 splice @{$self->{open_elements}}, $_, 1;
4869 wakaba 1.1 $i-- and last OE if defined $i;
4870 wakaba 1.3 } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4871 wakaba 1.79
4872 wakaba 1.1 $i = $_;
4873     }
4874     } # OE
4875 wakaba 1.3 splice @{$self->{open_elements}}, $i + 1, 1, $clone;
4876 wakaba 1.1
4877     ## Step 14
4878     redo FET;
4879     } # FET
4880     }; # $formatting_end_tag
4881    
4882 wakaba 1.96 $insert = my $insert_to_current = sub {
4883 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4884 wakaba 1.1 }; # $insert_to_current
4885    
4886     my $insert_to_foster = sub {
4887 wakaba 1.95 my $child = shift;
4888 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4889 wakaba 1.95 # MUST
4890     my $foster_parent_element;
4891     my $next_sibling;
4892 wakaba 1.121 OE: for (reverse 0..$#{$self->{open_elements}}) {
4893     if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4894 wakaba 1.3 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4895 wakaba 1.1 if (defined $parent and $parent->node_type == 1) {
4896 wakaba 1.79
4897 wakaba 1.1 $foster_parent_element = $parent;
4898 wakaba 1.3 $next_sibling = $self->{open_elements}->[$_]->[0];
4899 wakaba 1.1 } else {
4900 wakaba 1.79
4901 wakaba 1.1 $foster_parent_element
4902 wakaba 1.3 = $self->{open_elements}->[$_ - 1]->[0];
4903 wakaba 1.1 }
4904     last OE;
4905     }
4906     } # OE
4907 wakaba 1.3 $foster_parent_element = $self->{open_elements}->[0]->[0]
4908 wakaba 1.1 unless defined $foster_parent_element;
4909     $foster_parent_element->insert_before
4910     ($child, $next_sibling);
4911 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
4912     } else {
4913    
4914     $self->{open_elements}->[-1]->[0]->append_child ($child);
4915     }
4916 wakaba 1.1 }; # $insert_to_foster
4917    
4918 wakaba 1.123 B: while (1) {
4919 wakaba 1.57 if ($token->{type} == DOCTYPE_TOKEN) {
4920 wakaba 1.79
4921 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'DOCTYPE in the middle', token => $token);
4922 wakaba 1.54 ## Ignore the token
4923     ## Stay in the phase
4924     $token = $self->_get_next_token;
4925 wakaba 1.123 next B;
4926 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN and
4927 wakaba 1.54 $token->{tag_name} eq 'html') {
4928 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4929 wakaba 1.79
4930 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after html:html', token => $token);
4931 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
4932     } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4933 wakaba 1.79
4934 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after html:html', token => $token);
4935 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
4936 wakaba 1.79 } else {
4937    
4938 wakaba 1.54 }
4939    
4940 wakaba 1.84
4941 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'not first start tag', token => $token);
4942 wakaba 1.54 my $top_el = $self->{open_elements}->[0]->[0];
4943     for my $attr_name (keys %{$token->{attributes}}) {
4944     unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4945 wakaba 1.79
4946 wakaba 1.54 $top_el->set_attribute_ns
4947     (undef, [undef, $attr_name],
4948     $token->{attributes}->{$attr_name}->{value});
4949     }
4950     }
4951 wakaba 1.122
4952 wakaba 1.54 $token = $self->_get_next_token;
4953 wakaba 1.123 next B;
4954 wakaba 1.57 } elsif ($token->{type} == COMMENT_TOKEN) {
4955 wakaba 1.54 my $comment = $self->{document}->create_comment ($token->{data});
4956 wakaba 1.58 if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4957 wakaba 1.79
4958 wakaba 1.54 $self->{document}->append_child ($comment);
4959 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4960 wakaba 1.79
4961 wakaba 1.54 $self->{open_elements}->[0]->[0]->append_child ($comment);
4962     } else {
4963 wakaba 1.79
4964 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($comment);
4965     }
4966     $token = $self->_get_next_token;
4967 wakaba 1.123 next B;
4968     } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4969     if ($token->{type} == CHARACTER_TOKEN) {
4970    
4971     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4972     $token = $self->_get_next_token;
4973     next B;
4974     } elsif ($token->{type} == START_TAG_TOKEN) {
4975 wakaba 1.126 if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4976     $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4977 wakaba 1.123 not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4978     ($token->{tag_name} eq 'svg' and
4979     $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4980     ## NOTE: "using the rules for secondary insertion mode"then"continue"
4981    
4982     #
4983     } elsif ({
4984 wakaba 1.127 b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4985     center => 1, code => 1, dd => 1, div => 1, dl => 1, em => 1,
4986     embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1, ## No h4!
4987     h5 => 1, h6 => 1, head => 1, hr => 1, i => 1, img => 1,
4988     li => 1, menu => 1, meta => 1, nobr => 1, p => 1, pre => 1,
4989     ruby => 1, s => 1, small => 1, span => 1, strong => 1,
4990     sub => 1, sup => 1, table => 1, tt => 1, u => 1, ul => 1,
4991     var => 1,
4992 wakaba 1.123 }->{$token->{tag_name}}) {
4993    
4994     $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
4995     value => $self->{open_elements}->[-1]->[0]
4996     ->manakai_local_name,
4997     token => $token);
4998    
4999     pop @{$self->{open_elements}}
5000     while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
5001    
5002 wakaba 1.127 $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
5003 wakaba 1.123 ## Reprocess.
5004     next B;
5005     } else {
5006 wakaba 1.128 my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
5007     my $tag_name = $token->{tag_name};
5008     if ($nsuri eq $SVG_NS) {
5009     $tag_name = {
5010     altglyph => 'altGlyph',
5011     altglyphdef => 'altGlyphDef',
5012     altglyphitem => 'altGlyphItem',
5013     animatecolor => 'animateColor',
5014     animatemotion => 'animateMotion',
5015     animatetransform => 'animateTransform',
5016     clippath => 'clipPath',
5017     feblend => 'feBlend',
5018     fecolormatrix => 'feColorMatrix',
5019     fecomponenttransfer => 'feComponentTransfer',
5020     fecomposite => 'feComposite',
5021     feconvolvematrix => 'feConvolveMatrix',
5022     fediffuselighting => 'feDiffuseLighting',
5023     fedisplacementmap => 'feDisplacementMap',
5024     fedistantlight => 'feDistantLight',
5025     feflood => 'feFlood',
5026     fefunca => 'feFuncA',
5027     fefuncb => 'feFuncB',
5028     fefuncg => 'feFuncG',
5029     fefuncr => 'feFuncR',
5030     fegaussianblur => 'feGaussianBlur',
5031     feimage => 'feImage',
5032     femerge => 'feMerge',
5033     femergenode => 'feMergeNode',
5034     femorphology => 'feMorphology',
5035     feoffset => 'feOffset',
5036     fepointlight => 'fePointLight',
5037     fespecularlighting => 'feSpecularLighting',
5038     fespotlight => 'feSpotLight',
5039     fetile => 'feTile',
5040     feturbulence => 'feTurbulence',
5041     foreignobject => 'foreignObject',
5042     glyphref => 'glyphRef',
5043     lineargradient => 'linearGradient',
5044     radialgradient => 'radialGradient',
5045     #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
5046     textpath => 'textPath',
5047     }->{$tag_name} || $tag_name;
5048     }
5049    
5050     ## "adjust SVG attributes" (SVG only) - done in insert-element-f
5051    
5052     ## "adjust foreign attributes" - done in insert-element-f
5053 wakaba 1.123
5054    
5055     {
5056     my $el;
5057    
5058     $el = $self->{document}->create_element_ns
5059 wakaba 1.128 ($nsuri, [undef, $tag_name]);
5060 wakaba 1.123
5061     for my $attr_name (keys %{ $token->{attributes}}) {
5062     my $attr_t = $token->{attributes}->{$attr_name};
5063 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (
5064     @{
5065     $foreign_attr_xname->{$attr_name} ||
5066     [undef, [undef,
5067     $nsuri eq $SVG_NS ?
5068     ($svg_attr_name->{$attr_name} || $attr_name) :
5069     $attr_name]]
5070     }
5071     );
5072 wakaba 1.123 $attr->value ($attr_t->{value});
5073     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5074     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5075     $el->set_attribute_node_ns ($attr);
5076     }
5077    
5078     $el->set_user_data (manakai_source_line => $token->{line})
5079     if defined $token->{line};
5080     $el->set_user_data (manakai_source_column => $token->{column})
5081     if defined $token->{column};
5082    
5083     $insert->($el);
5084 wakaba 1.128 push @{$self->{open_elements}}, [$el, ($el_category_f->{$nsuri}->{ $tag_name} || 0) | FOREIGN_EL];
5085    
5086     if ( $token->{attributes}->{xmlns} and $token->{attributes}->{xmlns}->{value} ne ($nsuri)) {
5087     $self->{parse_error}->(level => $self->{must_level}, type => 'bad namespace', token => $token);
5088     ## TODO: Error type documentation
5089     }
5090 wakaba 1.123 }
5091    
5092    
5093     if ($self->{self_closing}) {
5094     pop @{$self->{open_elements}};
5095     delete $self->{self_closing};
5096     } else {
5097    
5098     }
5099    
5100     $token = $self->_get_next_token;
5101     next B;
5102     }
5103     } elsif ($token->{type} == END_TAG_TOKEN) {
5104     ## NOTE: "using the rules for secondary insertion mode" then "continue"
5105    
5106     #
5107     } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5108     ## NOTE: "using the rules for secondary insertion mode" then "continue"
5109    
5110     #
5111     ## TODO: ...
5112     } else {
5113     die "$0: $token->{type}: Unknown token type";
5114     }
5115     }
5116    
5117     if ($self->{insertion_mode} & HEAD_IMS) {
5118 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
5119 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5120 wakaba 1.99 unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5121    
5122     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5123     } else {
5124    
5125     ## Ignore the token.
5126     $token = $self->_get_next_token;
5127 wakaba 1.123 next B;
5128 wakaba 1.99 }
5129 wakaba 1.54 unless (length $token->{data}) {
5130 wakaba 1.79
5131 wakaba 1.54 $token = $self->_get_next_token;
5132 wakaba 1.123 next B;
5133 wakaba 1.54 }
5134     }
5135    
5136 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5137 wakaba 1.79
5138 wakaba 1.54 ## As if <head>
5139    
5140     $self->{head_element} = $self->{document}->create_element_ns
5141 wakaba 1.128 ($HTML_NS, [undef, 'head']);
5142 wakaba 1.54
5143 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
5144     if defined $token->{line};
5145     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
5146     if defined $token->{column};
5147    
5148 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5149 wakaba 1.121 push @{$self->{open_elements}},
5150     [$self->{head_element}, $el_category->{head}];
5151 wakaba 1.54
5152     ## Reprocess in the "in head" insertion mode...
5153     pop @{$self->{open_elements}};
5154    
5155     ## Reprocess in the "after head" insertion mode...
5156 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5157 wakaba 1.79
5158 wakaba 1.54 ## As if </noscript>
5159     pop @{$self->{open_elements}};
5160 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:#character', token => $token);
5161 wakaba 1.54
5162     ## Reprocess in the "in head" insertion mode...
5163     ## As if </head>
5164     pop @{$self->{open_elements}};
5165    
5166     ## Reprocess in the "after head" insertion mode...
5167 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5168 wakaba 1.79
5169 wakaba 1.54 pop @{$self->{open_elements}};
5170    
5171     ## Reprocess in the "after head" insertion mode...
5172 wakaba 1.79 } else {
5173    
5174 wakaba 1.54 }
5175    
5176 wakaba 1.121 ## "after head" insertion mode
5177     ## As if <body>
5178    
5179 wakaba 1.54 {
5180     my $el;
5181    
5182     $el = $self->{document}->create_element_ns
5183 wakaba 1.128 ($HTML_NS, [undef, 'body']);
5184 wakaba 1.54
5185 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5186     if defined $token->{line};
5187     $el->set_user_data (manakai_source_column => $token->{column})
5188     if defined $token->{column};
5189    
5190 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
5191 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
5192 wakaba 1.54 }
5193    
5194 wakaba 1.121 $self->{insertion_mode} = IN_BODY_IM;
5195     ## reprocess
5196 wakaba 1.123 next B;
5197 wakaba 1.121 } elsif ($token->{type} == START_TAG_TOKEN) {
5198     if ($token->{tag_name} eq 'head') {
5199     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5200    
5201    
5202 wakaba 1.54 $self->{head_element} = $self->{document}->create_element_ns
5203 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5204 wakaba 1.54
5205     for my $attr_name (keys %{ $token->{attributes}}) {
5206 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5207 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5208 wakaba 1.117 $attr->value ($attr_t->{value});
5209     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5210     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5211     $self->{head_element}->set_attribute_node_ns ($attr);
5212 wakaba 1.54 }
5213    
5214 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
5215     if defined $token->{line};
5216     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
5217     if defined $token->{column};
5218    
5219 wakaba 1.121 $self->{open_elements}->[-1]->[0]->append_child
5220     ($self->{head_element});
5221     push @{$self->{open_elements}},
5222     [$self->{head_element}, $el_category->{head}];
5223     $self->{insertion_mode} = IN_HEAD_IM;
5224 wakaba 1.122
5225     $token = $self->_get_next_token;
5226 wakaba 1.123 next B;
5227 wakaba 1.122 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5228    
5229 wakaba 1.136 $self->{parse_error}->(level => $self->{must_level}, type => 'after head:head', token => $token); ## TODO: error type
5230     ## Ignore the token
5231    
5232     $token = $self->_get_next_token;
5233     next B;
5234 wakaba 1.122 } else {
5235    
5236     $self->{parse_error}->(level => $self->{must_level}, type => 'in head:head', token => $token); # or in head noscript
5237     ## Ignore the token
5238    
5239 wakaba 1.121 $token = $self->_get_next_token;
5240 wakaba 1.123 next B;
5241 wakaba 1.122 }
5242     } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5243 wakaba 1.123
5244     ## As if <head>
5245    
5246 wakaba 1.54 $self->{head_element} = $self->{document}->create_element_ns
5247 wakaba 1.128 ($HTML_NS, [undef, 'head']);
5248 wakaba 1.54
5249 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
5250     if defined $token->{line};
5251     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
5252     if defined $token->{column};
5253    
5254 wakaba 1.123 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5255     push @{$self->{open_elements}},
5256     [$self->{head_element}, $el_category->{head}];
5257 wakaba 1.54
5258 wakaba 1.123 $self->{insertion_mode} = IN_HEAD_IM;
5259     ## Reprocess in the "in head" insertion mode...
5260     } else {
5261    
5262     }
5263 wakaba 1.54
5264     if ($token->{tag_name} eq 'base') {
5265 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5266 wakaba 1.79
5267 wakaba 1.54 ## As if </noscript>
5268     pop @{$self->{open_elements}};
5269 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:base', token => $token);
5270 wakaba 1.54
5271 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
5272 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
5273 wakaba 1.79 } else {
5274    
5275 wakaba 1.54 }
5276    
5277     ## NOTE: There is a "as if in head" code clone.
5278 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
5279 wakaba 1.79
5280 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after head:'.$token->{tag_name}, token => $token);
5281 wakaba 1.121 push @{$self->{open_elements}},
5282     [$self->{head_element}, $el_category->{head}];
5283 wakaba 1.79 } else {
5284    
5285 wakaba 1.54 }
5286    
5287     {
5288     my $el;
5289    
5290     $el = $self->{document}->create_element_ns
5291 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5292 wakaba 1.54
5293     for my $attr_name (keys %{ $token->{attributes}}) {
5294 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5295 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5296 wakaba 1.117 $attr->value ($attr_t->{value});
5297     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5298     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5299     $el->set_attribute_node_ns ($attr);
5300 wakaba 1.54 }
5301    
5302 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5303     if defined $token->{line};
5304     $el->set_user_data (manakai_source_column => $token->{column})
5305     if defined $token->{column};
5306    
5307 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
5308 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
5309 wakaba 1.54 }
5310    
5311     pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5312 wakaba 1.100 pop @{$self->{open_elements}} # <head>
5313 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
5314 wakaba 1.122
5315 wakaba 1.54 $token = $self->_get_next_token;
5316 wakaba 1.123 next B;
5317 wakaba 1.54 } elsif ($token->{tag_name} eq 'link') {
5318     ## NOTE: There is a "as if in head" code clone.
5319 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
5320 wakaba 1.79
5321 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after head:'.$token->{tag_name}, token => $token);
5322 wakaba 1.121 push @{$self->{open_elements}},
5323     [$self->{head_element}, $el_category->{head}];
5324 wakaba 1.79 } else {
5325    
5326 wakaba 1.54 }
5327    
5328 wakaba 1.25 {
5329     my $el;
5330    
5331 wakaba 1.1 $el = $self->{document}->create_element_ns
5332 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5333 wakaba 1.1
5334 wakaba 1.25 for my $attr_name (keys %{ $token->{attributes}}) {
5335 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5336 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5337 wakaba 1.117 $attr->value ($attr_t->{value});
5338     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5339     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5340     $el->set_attribute_node_ns ($attr);
5341 wakaba 1.1 }
5342    
5343 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5344     if defined $token->{line};
5345     $el->set_user_data (manakai_source_column => $token->{column})
5346     if defined $token->{column};
5347    
5348 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
5349 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
5350 wakaba 1.25 }
5351    
5352 wakaba 1.54 pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5353 wakaba 1.100 pop @{$self->{open_elements}} # <head>
5354 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
5355 wakaba 1.122 delete $self->{self_closing};
5356 wakaba 1.54 $token = $self->_get_next_token;
5357 wakaba 1.123 next B;
5358 wakaba 1.54 } elsif ($token->{tag_name} eq 'meta') {
5359     ## NOTE: There is a "as if in head" code clone.
5360 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
5361 wakaba 1.79
5362 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after head:'.$token->{tag_name}, token => $token);
5363 wakaba 1.121 push @{$self->{open_elements}},
5364     [$self->{head_element}, $el_category->{head}];
5365 wakaba 1.79 } else {
5366    
5367 wakaba 1.54 }
5368    
5369 wakaba 1.34 {
5370     my $el;
5371    
5372     $el = $self->{document}->create_element_ns
5373 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5374 wakaba 1.34
5375     for my $attr_name (keys %{ $token->{attributes}}) {
5376 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5377 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5378 wakaba 1.117 $attr->value ($attr_t->{value});
5379     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5380     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5381     $el->set_attribute_node_ns ($attr);
5382 wakaba 1.34 }
5383    
5384 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5385     if defined $token->{line};
5386     $el->set_user_data (manakai_source_column => $token->{column})
5387     if defined $token->{column};
5388    
5389 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
5390 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
5391 wakaba 1.34 }
5392    
5393 wakaba 1.68 my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5394 wakaba 1.34
5395 wakaba 1.54 unless ($self->{confident}) {
5396 wakaba 1.131 if ($token->{attributes}->{charset}) {
5397 wakaba 1.79
5398 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
5399     ## in the {change_encoding} callback.
5400 wakaba 1.65 $self->{change_encoding}
5401 wakaba 1.112 ->($self, $token->{attributes}->{charset}->{value},
5402     $token);
5403 wakaba 1.68
5404     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5405     ->set_user_data (manakai_has_reference =>
5406     $token->{attributes}->{charset}
5407     ->{has_reference});
5408 wakaba 1.65 } elsif ($token->{attributes}->{content}) {
5409     if ($token->{attributes}->{content}->{value}
5410 wakaba 1.71 =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
5411     [\x09-\x0D\x20]*=
5412 wakaba 1.54 [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
5413     ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
5414 wakaba 1.79
5415 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
5416     ## in the {change_encoding} callback.
5417 wakaba 1.65 $self->{change_encoding}
5418 wakaba 1.112 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
5419     $token);
5420 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5421     ->set_user_data (manakai_has_reference =>
5422     $token->{attributes}->{content}
5423     ->{has_reference});
5424 wakaba 1.79 } else {
5425    
5426 wakaba 1.65 }
5427 wakaba 1.54 }
5428 wakaba 1.68 } else {
5429     if ($token->{attributes}->{charset}) {
5430 wakaba 1.79
5431 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5432     ->set_user_data (manakai_has_reference =>
5433     $token->{attributes}->{charset}
5434     ->{has_reference});
5435     }
5436 wakaba 1.69 if ($token->{attributes}->{content}) {
5437 wakaba 1.79
5438 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5439     ->set_user_data (manakai_has_reference =>
5440     $token->{attributes}->{content}
5441     ->{has_reference});
5442     }
5443 wakaba 1.54 }
5444 wakaba 1.34
5445 wakaba 1.100 pop @{$self->{open_elements}} # <head>
5446 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
5447 wakaba 1.122 delete $self->{self_closing};
5448 wakaba 1.54 $token = $self->_get_next_token;
5449 wakaba 1.123 next B;
5450 wakaba 1.54 } elsif ($token->{tag_name} eq 'title') {
5451 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5452 wakaba 1.79
5453 wakaba 1.54 ## As if </noscript>
5454     pop @{$self->{open_elements}};
5455 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:title', token => $token);
5456 wakaba 1.1
5457 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
5458 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
5459 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5460 wakaba 1.79
5461 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after head:'.$token->{tag_name}, token => $token);
5462 wakaba 1.121 push @{$self->{open_elements}},
5463     [$self->{head_element}, $el_category->{head}];
5464 wakaba 1.79 } else {
5465    
5466 wakaba 1.54 }
5467    
5468     ## NOTE: There is a "as if in head" code clone.
5469     my $parent = defined $self->{head_element} ? $self->{head_element}
5470     : $self->{open_elements}->[-1]->[0];
5471 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
5472 wakaba 1.100 pop @{$self->{open_elements}} # <head>
5473 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
5474 wakaba 1.123 next B;
5475 wakaba 1.54 } elsif ($token->{tag_name} eq 'style') {
5476     ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
5477 wakaba 1.56 ## insertion mode IN_HEAD_IM)
5478 wakaba 1.54 ## NOTE: There is a "as if in head" code clone.
5479 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
5480 wakaba 1.79
5481 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after head:'.$token->{tag_name}, token => $token);
5482 wakaba 1.121 push @{$self->{open_elements}},
5483     [$self->{head_element}, $el_category->{head}];
5484 wakaba 1.79 } else {
5485    
5486 wakaba 1.54 }
5487 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
5488 wakaba 1.100 pop @{$self->{open_elements}} # <head>
5489 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
5490 wakaba 1.123 next B;
5491 wakaba 1.54 } elsif ($token->{tag_name} eq 'noscript') {
5492 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_IM) {
5493 wakaba 1.79
5494 wakaba 1.54 ## NOTE: and scripting is disalbed
5495    
5496 wakaba 1.1 {
5497     my $el;
5498    
5499     $el = $self->{document}->create_element_ns
5500 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5501 wakaba 1.1
5502     for my $attr_name (keys %{ $token->{attributes}}) {
5503 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5504 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5505 wakaba 1.117 $attr->value ($attr_t->{value});
5506     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5507     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5508     $el->set_attribute_node_ns ($attr);
5509 wakaba 1.1 }
5510    
5511 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5512     if defined $token->{line};
5513     $el->set_user_data (manakai_source_column => $token->{column})
5514     if defined $token->{column};
5515    
5516 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
5517 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
5518 wakaba 1.1 }
5519    
5520 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
5521 wakaba 1.122
5522 wakaba 1.54 $token = $self->_get_next_token;
5523 wakaba 1.123 next B;
5524 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5525 wakaba 1.79
5526 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:noscript', token => $token);
5527 wakaba 1.54 ## Ignore the token
5528 wakaba 1.122
5529 wakaba 1.54 $token = $self->_get_next_token;
5530 wakaba 1.123 next B;
5531 wakaba 1.54 } else {
5532 wakaba 1.79
5533 wakaba 1.54 #
5534     }
5535     } elsif ($token->{tag_name} eq 'script') {
5536 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5537 wakaba 1.79
5538 wakaba 1.54 ## As if </noscript>
5539     pop @{$self->{open_elements}};
5540 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:script', token => $token);
5541 wakaba 1.54
5542 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
5543 wakaba 1.54 ## Reprocess in the "in head" insertion mode...
5544 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5545 wakaba 1.79
5546 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after head:'.$token->{tag_name}, token => $token);
5547 wakaba 1.121 push @{$self->{open_elements}},
5548     [$self->{head_element}, $el_category->{head}];
5549 wakaba 1.79 } else {
5550    
5551 wakaba 1.54 }
5552    
5553     ## NOTE: There is a "as if in head" code clone.
5554 wakaba 1.100 $script_start_tag->();
5555     pop @{$self->{open_elements}} # <head>
5556 wakaba 1.56 if $self->{insertion_mode} == AFTER_HEAD_IM;
5557 wakaba 1.123 next B;
5558 wakaba 1.54 } elsif ($token->{tag_name} eq 'body' or
5559     $token->{tag_name} eq 'frameset') {
5560 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5561 wakaba 1.79
5562 wakaba 1.54 ## As if </noscript>
5563     pop @{$self->{open_elements}};
5564 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:'.$token->{tag_name}, token => $token);
5565 wakaba 1.54
5566     ## Reprocess in the "in head" insertion mode...
5567     ## As if </head>
5568     pop @{$self->{open_elements}};
5569    
5570     ## Reprocess in the "after head" insertion mode...
5571 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5572 wakaba 1.79
5573 wakaba 1.54 pop @{$self->{open_elements}};
5574    
5575     ## Reprocess in the "after head" insertion mode...
5576 wakaba 1.79 } else {
5577    
5578 wakaba 1.54 }
5579    
5580     ## "after head" insertion mode
5581    
5582 wakaba 1.1 {
5583     my $el;
5584    
5585     $el = $self->{document}->create_element_ns
5586 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
5587 wakaba 1.1
5588     for my $attr_name (keys %{ $token->{attributes}}) {
5589 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
5590 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
5591 wakaba 1.117 $attr->value ($attr_t->{value});
5592     $attr->set_user_data (manakai_source_line => $attr_t->{line});
5593     $attr->set_user_data (manakai_source_column => $attr_t->{column});
5594     $el->set_attribute_node_ns ($attr);
5595 wakaba 1.1 }
5596    
5597 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5598     if defined $token->{line};
5599     $el->set_user_data (manakai_source_column => $token->{column})
5600     if defined $token->{column};
5601    
5602 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
5603 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
5604 wakaba 1.1 }
5605    
5606 wakaba 1.56 if ($token->{tag_name} eq 'body') {
5607 wakaba 1.79
5608 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
5609     } elsif ($token->{tag_name} eq 'frameset') {
5610 wakaba 1.79
5611 wakaba 1.56 $self->{insertion_mode} = IN_FRAMESET_IM;
5612     } else {
5613     die "$0: tag name: $self->{tag_name}";
5614     }
5615 wakaba 1.122
5616 wakaba 1.54 $token = $self->_get_next_token;
5617 wakaba 1.123 next B;
5618 wakaba 1.54 } else {
5619 wakaba 1.79
5620 wakaba 1.54 #
5621 wakaba 1.8 }
5622 wakaba 1.54
5623 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5624 wakaba 1.79
5625 wakaba 1.54 ## As if </noscript>
5626     pop @{$self->{open_elements}};
5627 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:/'.$token->{tag_name}, token => $token);
5628 wakaba 1.54
5629     ## Reprocess in the "in head" insertion mode...
5630     ## As if </head>
5631     pop @{$self->{open_elements}};
5632    
5633     ## Reprocess in the "after head" insertion mode...
5634 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5635 wakaba 1.79
5636 wakaba 1.54 ## As if </head>
5637     pop @{$self->{open_elements}};
5638    
5639     ## Reprocess in the "after head" insertion mode...
5640 wakaba 1.79 } else {
5641    
5642 wakaba 1.54 }
5643    
5644     ## "after head" insertion mode
5645     ## As if <body>
5646    
5647 wakaba 1.1 {
5648     my $el;
5649    
5650     $el = $self->{document}->create_element_ns
5651 wakaba 1.128 ($HTML_NS, [undef, 'body']);
5652 wakaba 1.1
5653 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5654     if defined $token->{line};
5655     $el->set_user_data (manakai_source_column => $token->{column})
5656     if defined $token->{column};
5657    
5658 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
5659 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
5660 wakaba 1.1 }
5661    
5662 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
5663 wakaba 1.54 ## reprocess
5664 wakaba 1.122
5665 wakaba 1.123 next B;
5666 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
5667 wakaba 1.54 if ($token->{tag_name} eq 'head') {
5668 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5669 wakaba 1.79
5670 wakaba 1.54 ## As if <head>
5671    
5672     $self->{head_element} = $self->{document}->create_element_ns
5673 wakaba 1.128 ($HTML_NS, [undef, 'head']);
5674 wakaba 1.54
5675 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
5676     if defined $token->{line};
5677     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
5678     if defined $token->{column};
5679    
5680 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5681 wakaba 1.121 push @{$self->{open_elements}},
5682     [$self->{head_element}, $el_category->{head}];
5683 wakaba 1.54
5684     ## Reprocess in the "in head" insertion mode...
5685     pop @{$self->{open_elements}};
5686 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
5687 wakaba 1.54 $token = $self->_get_next_token;
5688 wakaba 1.123 next B;
5689 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5690 wakaba 1.79
5691 wakaba 1.54 ## As if </noscript>
5692     pop @{$self->{open_elements}};
5693 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:/head', token => $token);
5694 wakaba 1.54
5695     ## Reprocess in the "in head" insertion mode...
5696     pop @{$self->{open_elements}};
5697 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
5698 wakaba 1.54 $token = $self->_get_next_token;
5699 wakaba 1.123 next B;
5700 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5701 wakaba 1.79
5702 wakaba 1.54 pop @{$self->{open_elements}};
5703 wakaba 1.56 $self->{insertion_mode} = AFTER_HEAD_IM;
5704 wakaba 1.54 $token = $self->_get_next_token;
5705 wakaba 1.123 next B;
5706 wakaba 1.136 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5707    
5708     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:head', token => $token);
5709     ## Ignore the token
5710     $token = $self->_get_next_token;
5711     next B;
5712 wakaba 1.54 } else {
5713 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
5714 wakaba 1.54 }
5715     } elsif ($token->{tag_name} eq 'noscript') {
5716 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5717 wakaba 1.79
5718 wakaba 1.54 pop @{$self->{open_elements}};
5719 wakaba 1.56 $self->{insertion_mode} = IN_HEAD_IM;
5720 wakaba 1.54 $token = $self->_get_next_token;
5721 wakaba 1.123 next B;
5722 wakaba 1.136 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5723     $self->{insertion_mode} == AFTER_HEAD_IM) {
5724 wakaba 1.79
5725 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:noscript', token => $token);
5726 wakaba 1.54 ## Ignore the token ## ISSUE: An issue in the spec.
5727     $token = $self->_get_next_token;
5728 wakaba 1.123 next B;
5729 wakaba 1.54 } else {
5730 wakaba 1.79
5731 wakaba 1.54 #
5732     }
5733     } elsif ({
5734     body => 1, html => 1,
5735     }->{$token->{tag_name}}) {
5736 wakaba 1.136 if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5737     $self->{insertion_mode} == IN_HEAD_IM or
5738     $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5739 wakaba 1.79
5740 wakaba 1.136 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5741     ## Ignore the token
5742     $token = $self->_get_next_token;
5743     next B;
5744     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5745 wakaba 1.79
5746 wakaba 1.136 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:' . $token->{tag_name}, token => $token);
5747 wakaba 1.54 ## Ignore the token
5748     $token = $self->_get_next_token;
5749 wakaba 1.123 next B;
5750 wakaba 1.79 } else {
5751 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
5752 wakaba 1.54 }
5753 wakaba 1.136 } elsif ($token->{tag_name} eq 'p') {
5754    
5755     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:p', token => $token);
5756     ## Ignore the token
5757     $token = $self->_get_next_token;
5758     next B;
5759     } elsif ($token->{tag_name} eq 'br') {
5760 wakaba 1.56 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5761 wakaba 1.79
5762 wakaba 1.136 ## (before head) as if <head>, (in head) as if </head>
5763 wakaba 1.54
5764     $self->{head_element} = $self->{document}->create_element_ns
5765 wakaba 1.128 ($HTML_NS, [undef, 'head']);
5766 wakaba 1.54
5767 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
5768     if defined $token->{line};
5769     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
5770     if defined $token->{column};
5771    
5772 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5773 wakaba 1.136 $self->{insertion_mode} = AFTER_HEAD_IM;
5774    
5775     ## Reprocess in the "after head" insertion mode...
5776     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5777    
5778     ## As if </head>
5779     pop @{$self->{open_elements}};
5780     $self->{insertion_mode} = AFTER_HEAD_IM;
5781    
5782     ## Reprocess in the "after head" insertion mode...
5783     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5784    
5785     ## ISSUE: Two parse errors for <head><noscript></br>
5786     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:br', token => $token);
5787     ## As if </noscript>
5788     pop @{$self->{open_elements}};
5789     $self->{insertion_mode} = IN_HEAD_IM;
5790 wakaba 1.54
5791     ## Reprocess in the "in head" insertion mode...
5792 wakaba 1.136 ## As if </head>
5793     pop @{$self->{open_elements}};
5794     $self->{insertion_mode} = AFTER_HEAD_IM;
5795 wakaba 1.54
5796 wakaba 1.136 ## Reprocess in the "after head" insertion mode...
5797     } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5798 wakaba 1.79
5799 wakaba 1.56 #
5800     } else {
5801 wakaba 1.136 die "$0: $self->{insertion_mode}: Unknown insertion mode";
5802 wakaba 1.54 }
5803 wakaba 1.136
5804     ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5805     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:br', token => $token);
5806     ## Ignore the token
5807     $token = $self->_get_next_token;
5808     next B;
5809     } else {
5810    
5811     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5812     ## Ignore the token
5813     $token = $self->_get_next_token;
5814     next B;
5815 wakaba 1.54 }
5816    
5817 wakaba 1.56 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5818 wakaba 1.79
5819 wakaba 1.54 ## As if </noscript>
5820     pop @{$self->{open_elements}};
5821 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:/'.$token->{tag_name}, token => $token);
5822 wakaba 1.54
5823     ## Reprocess in the "in head" insertion mode...
5824     ## As if </head>
5825     pop @{$self->{open_elements}};
5826    
5827     ## Reprocess in the "after head" insertion mode...
5828 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5829 wakaba 1.79
5830 wakaba 1.54 ## As if </head>
5831     pop @{$self->{open_elements}};
5832    
5833     ## Reprocess in the "after head" insertion mode...
5834 wakaba 1.56 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5835 wakaba 1.82 ## ISSUE: This case cannot be reached?
5836 wakaba 1.79
5837 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5838 wakaba 1.54 ## Ignore the token ## ISSUE: An issue in the spec.
5839     $token = $self->_get_next_token;
5840 wakaba 1.123 next B;
5841 wakaba 1.79 } else {
5842    
5843 wakaba 1.8 }
5844 wakaba 1.54
5845     ## "after head" insertion mode
5846     ## As if <body>
5847    
5848 wakaba 1.1 {
5849     my $el;
5850    
5851     $el = $self->{document}->create_element_ns
5852 wakaba 1.128 ($HTML_NS, [undef, 'body']);
5853 wakaba 1.1
5854 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5855     if defined $token->{line};
5856     $el->set_user_data (manakai_source_column => $token->{column})
5857     if defined $token->{column};
5858    
5859 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
5860 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
5861 wakaba 1.1 }
5862    
5863 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
5864 wakaba 1.54 ## reprocess
5865 wakaba 1.123 next B;
5866 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5867     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5868    
5869    
5870     ## NOTE: As if <head>
5871    
5872     $self->{head_element} = $self->{document}->create_element_ns
5873 wakaba 1.128 ($HTML_NS, [undef, 'head']);
5874 wakaba 1.103
5875 wakaba 1.114 $self->{head_element}->set_user_data (manakai_source_line => $token->{line})
5876     if defined $token->{line};
5877     $self->{head_element}->set_user_data (manakai_source_column => $token->{column})
5878     if defined $token->{column};
5879    
5880 wakaba 1.103 $self->{open_elements}->[-1]->[0]->append_child
5881     ($self->{head_element});
5882 wakaba 1.121 #push @{$self->{open_elements}},
5883     # [$self->{head_element}, $el_category->{head}];
5884 wakaba 1.103 #$self->{insertion_mode} = IN_HEAD_IM;
5885     ## NOTE: Reprocess.
5886    
5887     ## NOTE: As if </head>
5888     #pop @{$self->{open_elements}};
5889     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5890     ## NOTE: Reprocess.
5891    
5892     #
5893     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5894    
5895    
5896     ## NOTE: As if </head>
5897     pop @{$self->{open_elements}};
5898     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5899     ## NOTE: Reprocess.
5900    
5901     #
5902     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5903    
5904    
5905 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in noscript:#eof', token => $token);
5906 wakaba 1.103
5907     ## As if </noscript>
5908     pop @{$self->{open_elements}};
5909     #$self->{insertion_mode} = IN_HEAD_IM;
5910     ## NOTE: Reprocess.
5911    
5912     ## NOTE: As if </head>
5913     pop @{$self->{open_elements}};
5914     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5915     ## NOTE: Reprocess.
5916    
5917     #
5918     } else {
5919    
5920     #
5921     }
5922    
5923     ## NOTE: As if <body>
5924    
5925     {
5926     my $el;
5927    
5928     $el = $self->{document}->create_element_ns
5929 wakaba 1.128 ($HTML_NS, [undef, 'body']);
5930 wakaba 1.103
5931 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
5932     if defined $token->{line};
5933     $el->set_user_data (manakai_source_column => $token->{column})
5934     if defined $token->{column};
5935    
5936 wakaba 1.103 $self->{open_elements}->[-1]->[0]->append_child ($el);
5937 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'body'} || 0];
5938 wakaba 1.103 }
5939    
5940     $self->{insertion_mode} = IN_BODY_IM;
5941     ## NOTE: Reprocess.
5942 wakaba 1.123 next B;
5943 wakaba 1.103 } else {
5944     die "$0: $token->{type}: Unknown token type";
5945     }
5946 wakaba 1.54
5947     ## ISSUE: An issue in the spec.
5948 wakaba 1.58 } elsif ($self->{insertion_mode} & BODY_IMS) {
5949 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
5950 wakaba 1.79
5951 wakaba 1.54 ## NOTE: There is a code clone of "character in body".
5952     $reconstruct_active_formatting_elements->($insert_to_current);
5953    
5954     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5955    
5956     $token = $self->_get_next_token;
5957 wakaba 1.123 next B;
5958 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
5959 wakaba 1.54 if ({
5960     caption => 1, col => 1, colgroup => 1, tbody => 1,
5961     td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5962     }->{$token->{tag_name}}) {
5963 wakaba 1.56 if ($self->{insertion_mode} == IN_CELL_IM) {
5964 wakaba 1.54 ## have an element in table scope
5965 wakaba 1.106 for (reverse 0..$#{$self->{open_elements}}) {
5966 wakaba 1.54 my $node = $self->{open_elements}->[$_];
5967 wakaba 1.121 if ($node->[1] & TABLE_CELL_EL) {
5968 wakaba 1.79
5969 wakaba 1.106
5970     ## Close the cell
5971 wakaba 1.122
5972     $token->{self_closing} = $self->{self_closing};
5973     unshift @{$self->{token}}, $token;
5974     delete $self->{self_closing};
5975     # <x>
5976 wakaba 1.120 $token = {type => END_TAG_TOKEN,
5977     tag_name => $node->[0]->manakai_local_name,
5978 wakaba 1.112 line => $token->{line},
5979     column => $token->{column}};
5980 wakaba 1.123 next B;
5981 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
5982 wakaba 1.79
5983 wakaba 1.106 ## ISSUE: This case can never be reached, maybe.
5984     last;
5985 wakaba 1.54 }
5986 wakaba 1.106 }
5987    
5988 wakaba 1.54
5989 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'start tag not allowed',
5990 wakaba 1.111 value => $token->{tag_name}, token => $token);
5991 wakaba 1.106 ## Ignore the token
5992 wakaba 1.122
5993 wakaba 1.106 $token = $self->_get_next_token;
5994 wakaba 1.123 next B;
5995 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5996 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed:caption', token => $token);
5997 wakaba 1.54
5998 wakaba 1.106 ## NOTE: As if </caption>.
5999 wakaba 1.54 ## have a table element in table scope
6000     my $i;
6001 wakaba 1.106 INSCOPE: {
6002     for (reverse 0..$#{$self->{open_elements}}) {
6003     my $node = $self->{open_elements}->[$_];
6004 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
6005 wakaba 1.106
6006     $i = $_;
6007     last INSCOPE;
6008 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6009 wakaba 1.106
6010     last;
6011     }
6012 wakaba 1.54 }
6013 wakaba 1.106
6014    
6015     $self->{parse_error}->(level => $self->{must_level}, type => 'start tag not allowed',
6016 wakaba 1.111 value => $token->{tag_name}, token => $token);
6017 wakaba 1.106 ## Ignore the token
6018 wakaba 1.122
6019 wakaba 1.106 $token = $self->_get_next_token;
6020 wakaba 1.123 next B;
6021 wakaba 1.54 } # INSCOPE
6022    
6023     ## generate implied end tags
6024 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
6025     & END_TAG_OPTIONAL_EL) {
6026 wakaba 1.79
6027 wakaba 1.86 pop @{$self->{open_elements}};
6028 wakaba 1.54 }
6029    
6030 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
6031 wakaba 1.79
6032 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
6033     value => $self->{open_elements}->[-1]->[0]
6034     ->manakai_local_name,
6035     token => $token);
6036 wakaba 1.79 } else {
6037    
6038 wakaba 1.54 }
6039    
6040     splice @{$self->{open_elements}}, $i;
6041    
6042     $clear_up_to_marker->();
6043    
6044 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
6045 wakaba 1.54
6046     ## reprocess
6047 wakaba 1.122
6048 wakaba 1.123 next B;
6049 wakaba 1.54 } else {
6050 wakaba 1.79
6051 wakaba 1.54 #
6052     }
6053     } else {
6054 wakaba 1.79
6055 wakaba 1.54 #
6056     }
6057 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
6058 wakaba 1.54 if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
6059 wakaba 1.56 if ($self->{insertion_mode} == IN_CELL_IM) {
6060 wakaba 1.54 ## have an element in table scope
6061     my $i;
6062     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6063     my $node = $self->{open_elements}->[$_];
6064 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6065 wakaba 1.79
6066 wakaba 1.54 $i = $_;
6067     last INSCOPE;
6068 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6069 wakaba 1.79
6070 wakaba 1.54 last INSCOPE;
6071     }
6072     } # INSCOPE
6073     unless (defined $i) {
6074 wakaba 1.79
6075 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6076 wakaba 1.54 ## Ignore the token
6077     $token = $self->_get_next_token;
6078 wakaba 1.123 next B;
6079 wakaba 1.54 }
6080    
6081     ## generate implied end tags
6082 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
6083     & END_TAG_OPTIONAL_EL) {
6084 wakaba 1.79
6085 wakaba 1.86 pop @{$self->{open_elements}};
6086 wakaba 1.54 }
6087 wakaba 1.86
6088 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6089     ne $token->{tag_name}) {
6090 wakaba 1.79
6091 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
6092     value => $self->{open_elements}->[-1]->[0]
6093     ->manakai_local_name,
6094     token => $token);
6095 wakaba 1.79 } else {
6096    
6097 wakaba 1.54 }
6098    
6099     splice @{$self->{open_elements}}, $i;
6100    
6101     $clear_up_to_marker->();
6102    
6103 wakaba 1.56 $self->{insertion_mode} = IN_ROW_IM;
6104 wakaba 1.54
6105     $token = $self->_get_next_token;
6106 wakaba 1.123 next B;
6107 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
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 } else {
6114 wakaba 1.79
6115 wakaba 1.54 #
6116     }
6117     } elsif ($token->{tag_name} eq 'caption') {
6118 wakaba 1.56 if ($self->{insertion_mode} == IN_CAPTION_IM) {
6119 wakaba 1.54 ## have a table element in table scope
6120     my $i;
6121 wakaba 1.106 INSCOPE: {
6122     for (reverse 0..$#{$self->{open_elements}}) {
6123     my $node = $self->{open_elements}->[$_];
6124 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
6125 wakaba 1.106
6126     $i = $_;
6127     last INSCOPE;
6128 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6129 wakaba 1.106
6130     last;
6131     }
6132 wakaba 1.54 }
6133 wakaba 1.106
6134    
6135     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag',
6136 wakaba 1.111 value => $token->{tag_name}, token => $token);
6137 wakaba 1.106 ## Ignore the token
6138     $token = $self->_get_next_token;
6139 wakaba 1.123 next B;
6140 wakaba 1.54 } # INSCOPE
6141    
6142     ## generate implied end tags
6143 wakaba 1.121 while ($self->{open_elements}->[-1]->[1]
6144     & END_TAG_OPTIONAL_EL) {
6145 wakaba 1.79
6146 wakaba 1.86 pop @{$self->{open_elements}};
6147 wakaba 1.54 }
6148    
6149 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
6150 wakaba 1.79
6151 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
6152     value => $self->{open_elements}->[-1]->[0]
6153     ->manakai_local_name,
6154     token => $token);
6155 wakaba 1.79 } else {
6156    
6157 wakaba 1.54 }
6158    
6159     splice @{$self->{open_elements}}, $i;
6160    
6161     $clear_up_to_marker->();
6162    
6163 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
6164 wakaba 1.54
6165     $token = $self->_get_next_token;
6166 wakaba 1.123 next B;
6167 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_CELL_IM) {
6168 wakaba 1.79
6169 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6170 wakaba 1.54 ## Ignore the token
6171     $token = $self->_get_next_token;
6172 wakaba 1.123 next B;
6173 wakaba 1.54 } else {
6174 wakaba 1.79
6175 wakaba 1.54 #
6176 wakaba 1.1 }
6177 wakaba 1.54 } elsif ({
6178     table => 1, tbody => 1, tfoot => 1,
6179     thead => 1, tr => 1,
6180     }->{$token->{tag_name}} and
6181 wakaba 1.56 $self->{insertion_mode} == IN_CELL_IM) {
6182 wakaba 1.54 ## have an element in table scope
6183     my $i;
6184     my $tn;
6185 wakaba 1.106 INSCOPE: {
6186     for (reverse 0..$#{$self->{open_elements}}) {
6187     my $node = $self->{open_elements}->[$_];
6188 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6189 wakaba 1.106
6190     $i = $_;
6191    
6192     ## Close the cell
6193 wakaba 1.122
6194     $token->{self_closing} = $self->{self_closing};
6195     unshift @{$self->{token}}, $token;
6196     delete $self->{self_closing};
6197     # </x>
6198 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => $tn,
6199     line => $token->{line},
6200     column => $token->{column}};
6201 wakaba 1.123 next B;
6202 wakaba 1.121 } elsif ($node->[1] & TABLE_CELL_EL) {
6203 wakaba 1.106
6204 wakaba 1.121 $tn = $node->[0]->manakai_local_name;
6205 wakaba 1.106 ## NOTE: There is exactly one |td| or |th| element
6206     ## in scope in the stack of open elements by definition.
6207 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6208 wakaba 1.106 ## ISSUE: Can this be reached?
6209    
6210     last;
6211     }
6212 wakaba 1.54 }
6213 wakaba 1.106
6214 wakaba 1.79
6215 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag',
6216 wakaba 1.111 value => $token->{tag_name}, token => $token);
6217 wakaba 1.54 ## Ignore the token
6218     $token = $self->_get_next_token;
6219 wakaba 1.123 next B;
6220 wakaba 1.106 } # INSCOPE
6221 wakaba 1.54 } elsif ($token->{tag_name} eq 'table' and
6222 wakaba 1.56 $self->{insertion_mode} == IN_CAPTION_IM) {
6223 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed:caption', token => $token);
6224 wakaba 1.31
6225 wakaba 1.54 ## As if </caption>
6226     ## have a table element in table scope
6227     my $i;
6228     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6229     my $node = $self->{open_elements}->[$_];
6230 wakaba 1.121 if ($node->[1] & CAPTION_EL) {
6231 wakaba 1.79
6232 wakaba 1.54 $i = $_;
6233     last INSCOPE;
6234 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6235 wakaba 1.79
6236 wakaba 1.54 last INSCOPE;
6237     }
6238     } # INSCOPE
6239     unless (defined $i) {
6240 wakaba 1.79
6241 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:caption', token => $token);
6242 wakaba 1.54 ## Ignore the token
6243     $token = $self->_get_next_token;
6244 wakaba 1.123 next B;
6245 wakaba 1.54 }
6246    
6247     ## generate implied end tags
6248 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6249 wakaba 1.79
6250 wakaba 1.86 pop @{$self->{open_elements}};
6251 wakaba 1.54 }
6252 wakaba 1.20
6253 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
6254 wakaba 1.79
6255 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
6256     value => $self->{open_elements}->[-1]->[0]
6257     ->manakai_local_name,
6258     token => $token);
6259 wakaba 1.79 } else {
6260    
6261 wakaba 1.54 }
6262 wakaba 1.12
6263 wakaba 1.54 splice @{$self->{open_elements}}, $i;
6264 wakaba 1.31
6265 wakaba 1.54 $clear_up_to_marker->();
6266 wakaba 1.1
6267 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
6268 wakaba 1.3
6269 wakaba 1.54 ## reprocess
6270 wakaba 1.123 next B;
6271 wakaba 1.54 } elsif ({
6272     body => 1, col => 1, colgroup => 1, html => 1,
6273     }->{$token->{tag_name}}) {
6274 wakaba 1.58 if ($self->{insertion_mode} & BODY_TABLE_IMS) {
6275 wakaba 1.79
6276 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6277 wakaba 1.54 ## Ignore the token
6278     $token = $self->_get_next_token;
6279 wakaba 1.123 next B;
6280 wakaba 1.54 } else {
6281 wakaba 1.79
6282 wakaba 1.54 #
6283     }
6284     } elsif ({
6285     tbody => 1, tfoot => 1,
6286     thead => 1, tr => 1,
6287     }->{$token->{tag_name}} and
6288 wakaba 1.56 $self->{insertion_mode} == IN_CAPTION_IM) {
6289 wakaba 1.79
6290 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6291 wakaba 1.1 ## Ignore the token
6292     $token = $self->_get_next_token;
6293 wakaba 1.123 next B;
6294 wakaba 1.54 } else {
6295 wakaba 1.79
6296 wakaba 1.54 #
6297 wakaba 1.1 }
6298 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6299     for my $entry (@{$self->{open_elements}}) {
6300 wakaba 1.121 unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
6301 wakaba 1.103
6302 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in body:#eof', token => $token);
6303 wakaba 1.103 last;
6304     }
6305     }
6306    
6307     ## Stop parsing.
6308     last B;
6309 wakaba 1.54 } else {
6310     die "$0: $token->{type}: Unknown token type";
6311 wakaba 1.1 }
6312    
6313 wakaba 1.54 $insert = $insert_to_current;
6314     #
6315 wakaba 1.58 } elsif ($self->{insertion_mode} & TABLE_IMS) {
6316 wakaba 1.60 if ($token->{type} == CHARACTER_TOKEN) {
6317 wakaba 1.95 if (not $open_tables->[-1]->[1] and # tainted
6318     $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
6319     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6320 wakaba 1.54
6321 wakaba 1.95 unless (length $token->{data}) {
6322    
6323     $token = $self->_get_next_token;
6324 wakaba 1.123 next B;
6325 wakaba 1.95 } else {
6326    
6327     }
6328     }
6329 wakaba 1.1
6330 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in table:#character', token => $token);
6331 wakaba 1.1
6332 wakaba 1.54 ## As if in body, but insert into foster parent element
6333     ## ISSUE: Spec says that "whenever a node would be inserted
6334     ## into the current node" while characters might not be
6335     ## result in a new Text node.
6336     $reconstruct_active_formatting_elements->($insert_to_foster);
6337    
6338 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
6339 wakaba 1.54 # MUST
6340     my $foster_parent_element;
6341     my $next_sibling;
6342     my $prev_sibling;
6343     OE: for (reverse 0..$#{$self->{open_elements}}) {
6344 wakaba 1.121 if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
6345 wakaba 1.54 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
6346     if (defined $parent and $parent->node_type == 1) {
6347 wakaba 1.79
6348 wakaba 1.54 $foster_parent_element = $parent;
6349     $next_sibling = $self->{open_elements}->[$_]->[0];
6350     $prev_sibling = $next_sibling->previous_sibling;
6351     } else {
6352 wakaba 1.79
6353 wakaba 1.54 $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
6354     $prev_sibling = $foster_parent_element->last_child;
6355     }
6356     last OE;
6357     }
6358     } # OE
6359     $foster_parent_element = $self->{open_elements}->[0]->[0] and
6360     $prev_sibling = $foster_parent_element->last_child
6361     unless defined $foster_parent_element;
6362     if (defined $prev_sibling and
6363     $prev_sibling->node_type == 3) {
6364 wakaba 1.79
6365 wakaba 1.54 $prev_sibling->manakai_append_text ($token->{data});
6366     } else {
6367 wakaba 1.79
6368 wakaba 1.54 $foster_parent_element->insert_before
6369     ($self->{document}->create_text_node ($token->{data}),
6370     $next_sibling);
6371     }
6372 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
6373     } else {
6374    
6375     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6376     }
6377 wakaba 1.54
6378 wakaba 1.95 $token = $self->_get_next_token;
6379 wakaba 1.123 next B;
6380 wakaba 1.60 } elsif ($token->{type} == START_TAG_TOKEN) {
6381 wakaba 1.54 if ({
6382 wakaba 1.56 tr => ($self->{insertion_mode} != IN_ROW_IM),
6383 wakaba 1.54 th => 1, td => 1,
6384     }->{$token->{tag_name}}) {
6385 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_IM) {
6386 wakaba 1.54 ## Clear back to table context
6387 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6388     & TABLE_SCOPING_EL)) {
6389 wakaba 1.79
6390 wakaba 1.54 pop @{$self->{open_elements}};
6391     }
6392    
6393    
6394     {
6395     my $el;
6396    
6397     $el = $self->{document}->create_element_ns
6398 wakaba 1.128 ($HTML_NS, [undef, 'tbody']);
6399 wakaba 1.54
6400 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6401     if defined $token->{line};
6402     $el->set_user_data (manakai_source_column => $token->{column})
6403     if defined $token->{column};
6404    
6405 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6406 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'tbody'} || 0];
6407 wakaba 1.54 }
6408    
6409 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
6410 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
6411     }
6412    
6413 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6414 wakaba 1.54 unless ($token->{tag_name} eq 'tr') {
6415 wakaba 1.79
6416 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'missing start tag:tr', token => $token);
6417 wakaba 1.54 }
6418    
6419     ## Clear back to table body context
6420 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6421     & TABLE_ROWS_SCOPING_EL)) {
6422 wakaba 1.79
6423 wakaba 1.83 ## ISSUE: Can this case be reached?
6424 wakaba 1.54 pop @{$self->{open_elements}};
6425     }
6426    
6427 wakaba 1.56 $self->{insertion_mode} = IN_ROW_IM;
6428 wakaba 1.54 if ($token->{tag_name} eq 'tr') {
6429    
6430 wakaba 1.79
6431 wakaba 1.54 {
6432     my $el;
6433    
6434     $el = $self->{document}->create_element_ns
6435 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6436 wakaba 1.54
6437     for my $attr_name (keys %{ $token->{attributes}}) {
6438 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6439 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6440 wakaba 1.117 $attr->value ($attr_t->{value});
6441     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6442     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6443     $el->set_attribute_node_ns ($attr);
6444 wakaba 1.1 }
6445 wakaba 1.54
6446 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6447     if defined $token->{line};
6448     $el->set_user_data (manakai_source_column => $token->{column})
6449     if defined $token->{column};
6450    
6451 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6452 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6453 wakaba 1.54 }
6454    
6455 wakaba 1.122
6456 wakaba 1.54 $token = $self->_get_next_token;
6457 wakaba 1.123 next B;
6458 wakaba 1.54 } else {
6459    
6460 wakaba 1.79
6461 wakaba 1.54 {
6462     my $el;
6463    
6464     $el = $self->{document}->create_element_ns
6465 wakaba 1.128 ($HTML_NS, [undef, 'tr']);
6466 wakaba 1.54
6467 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6468     if defined $token->{line};
6469     $el->set_user_data (manakai_source_column => $token->{column})
6470     if defined $token->{column};
6471    
6472 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6473 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'tr'} || 0];
6474 wakaba 1.54 }
6475    
6476     ## reprocess in the "in row" insertion mode
6477     }
6478 wakaba 1.79 } else {
6479    
6480 wakaba 1.54 }
6481 wakaba 1.52
6482 wakaba 1.54 ## Clear back to table row context
6483 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6484     & TABLE_ROW_SCOPING_EL)) {
6485 wakaba 1.79
6486 wakaba 1.54 pop @{$self->{open_elements}};
6487     }
6488    
6489    
6490     {
6491     my $el;
6492    
6493     $el = $self->{document}->create_element_ns
6494 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6495 wakaba 1.1
6496 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
6497 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6498 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6499 wakaba 1.117 $attr->value ($attr_t->{value});
6500     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6501     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6502     $el->set_attribute_node_ns ($attr);
6503 wakaba 1.54 }
6504    
6505 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6506     if defined $token->{line};
6507     $el->set_user_data (manakai_source_column => $token->{column})
6508     if defined $token->{column};
6509    
6510 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6511 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6512 wakaba 1.54 }
6513    
6514 wakaba 1.56 $self->{insertion_mode} = IN_CELL_IM;
6515 wakaba 1.54
6516     push @$active_formatting_elements, ['#marker', ''];
6517    
6518 wakaba 1.122
6519 wakaba 1.54 $token = $self->_get_next_token;
6520 wakaba 1.123 next B;
6521 wakaba 1.54 } elsif ({
6522     caption => 1, col => 1, colgroup => 1,
6523     tbody => 1, tfoot => 1, thead => 1,
6524 wakaba 1.56 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6525 wakaba 1.54 }->{$token->{tag_name}}) {
6526 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
6527 wakaba 1.54 ## As if </tr>
6528     ## have an element in table scope
6529     my $i;
6530     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6531     my $node = $self->{open_elements}->[$_];
6532 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
6533 wakaba 1.79
6534 wakaba 1.54 $i = $_;
6535     last INSCOPE;
6536 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6537 wakaba 1.79
6538 wakaba 1.54 last INSCOPE;
6539     }
6540     } # INSCOPE
6541 wakaba 1.79 unless (defined $i) {
6542 wakaba 1.122
6543 wakaba 1.83 ## TODO: This type is wrong.
6544 wakaba 1.122 $self->{parse_error}->(level => $self->{must_level}, type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);
6545 wakaba 1.54 ## Ignore the token
6546 wakaba 1.122
6547 wakaba 1.54 $token = $self->_get_next_token;
6548 wakaba 1.123 next B;
6549 wakaba 1.54 }
6550    
6551     ## Clear back to table row context
6552 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6553     & TABLE_ROW_SCOPING_EL)) {
6554 wakaba 1.79
6555 wakaba 1.83 ## ISSUE: Can this case be reached?
6556 wakaba 1.54 pop @{$self->{open_elements}};
6557     }
6558    
6559     pop @{$self->{open_elements}}; # tr
6560 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
6561 wakaba 1.54 if ($token->{tag_name} eq 'tr') {
6562 wakaba 1.79
6563 wakaba 1.54 ## reprocess
6564 wakaba 1.122
6565 wakaba 1.123 next B;
6566 wakaba 1.54 } else {
6567 wakaba 1.79
6568 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
6569     }
6570     }
6571 wakaba 1.52
6572 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6573 wakaba 1.54 ## have an element in table scope
6574     my $i;
6575     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6576     my $node = $self->{open_elements}->[$_];
6577 wakaba 1.121 if ($node->[1] & TABLE_ROW_GROUP_EL) {
6578 wakaba 1.79
6579 wakaba 1.54 $i = $_;
6580     last INSCOPE;
6581 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6582 wakaba 1.79
6583 wakaba 1.54 last INSCOPE;
6584     }
6585     } # INSCOPE
6586     unless (defined $i) {
6587 wakaba 1.79
6588 wakaba 1.83 ## TODO: This erorr type ios wrong.
6589 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6590 wakaba 1.54 ## Ignore the token
6591 wakaba 1.122
6592 wakaba 1.54 $token = $self->_get_next_token;
6593 wakaba 1.123 next B;
6594 wakaba 1.54 }
6595 wakaba 1.52
6596 wakaba 1.54 ## Clear back to table body context
6597 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6598     & TABLE_ROWS_SCOPING_EL)) {
6599 wakaba 1.79
6600 wakaba 1.83 ## ISSUE: Can this state be reached?
6601 wakaba 1.54 pop @{$self->{open_elements}};
6602     }
6603    
6604     ## As if <{current node}>
6605     ## have an element in table scope
6606     ## true by definition
6607    
6608     ## Clear back to table body context
6609     ## nop by definition
6610    
6611     pop @{$self->{open_elements}};
6612 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
6613 wakaba 1.54 ## reprocess in "in table" insertion mode...
6614 wakaba 1.79 } else {
6615    
6616 wakaba 1.54 }
6617 wakaba 1.51
6618 wakaba 1.54 if ($token->{tag_name} eq 'col') {
6619     ## Clear back to table context
6620 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6621     & TABLE_SCOPING_EL)) {
6622 wakaba 1.79
6623 wakaba 1.83 ## ISSUE: Can this state be reached?
6624 wakaba 1.54 pop @{$self->{open_elements}};
6625     }
6626    
6627    
6628 wakaba 1.51 {
6629     my $el;
6630    
6631     $el = $self->{document}->create_element_ns
6632 wakaba 1.128 ($HTML_NS, [undef, 'colgroup']);
6633 wakaba 1.51
6634 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6635     if defined $token->{line};
6636     $el->set_user_data (manakai_source_column => $token->{column})
6637     if defined $token->{column};
6638    
6639 wakaba 1.51 $self->{open_elements}->[-1]->[0]->append_child ($el);
6640 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{'colgroup'} || 0];
6641 wakaba 1.51 }
6642    
6643 wakaba 1.56 $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
6644 wakaba 1.54 ## reprocess
6645 wakaba 1.122
6646 wakaba 1.123 next B;
6647 wakaba 1.54 } elsif ({
6648     caption => 1,
6649     colgroup => 1,
6650     tbody => 1, tfoot => 1, thead => 1,
6651     }->{$token->{tag_name}}) {
6652     ## Clear back to table context
6653 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6654     & TABLE_SCOPING_EL)) {
6655 wakaba 1.79
6656 wakaba 1.83 ## ISSUE: Can this state be reached?
6657 wakaba 1.54 pop @{$self->{open_elements}};
6658     }
6659    
6660     push @$active_formatting_elements, ['#marker', '']
6661     if $token->{tag_name} eq 'caption';
6662    
6663 wakaba 1.52
6664 wakaba 1.54 {
6665     my $el;
6666    
6667     $el = $self->{document}->create_element_ns
6668 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6669 wakaba 1.52
6670 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
6671 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6672 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6673 wakaba 1.117 $attr->value ($attr_t->{value});
6674     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6675     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6676     $el->set_attribute_node_ns ($attr);
6677 wakaba 1.52 }
6678    
6679 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6680     if defined $token->{line};
6681     $el->set_user_data (manakai_source_column => $token->{column})
6682     if defined $token->{column};
6683    
6684 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
6685 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6686 wakaba 1.54 }
6687    
6688     $self->{insertion_mode} = {
6689 wakaba 1.56 caption => IN_CAPTION_IM,
6690     colgroup => IN_COLUMN_GROUP_IM,
6691     tbody => IN_TABLE_BODY_IM,
6692     tfoot => IN_TABLE_BODY_IM,
6693     thead => IN_TABLE_BODY_IM,
6694 wakaba 1.54 }->{$token->{tag_name}};
6695 wakaba 1.52 $token = $self->_get_next_token;
6696 wakaba 1.122
6697 wakaba 1.123 next B;
6698 wakaba 1.54 } else {
6699     die "$0: in table: <>: $token->{tag_name}";
6700     }
6701     } elsif ($token->{tag_name} eq 'table') {
6702 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
6703     value => $self->{open_elements}->[-1]->[0]
6704     ->manakai_local_name,
6705     token => $token);
6706 wakaba 1.54
6707     ## As if </table>
6708     ## have a table element in table scope
6709     my $i;
6710     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6711     my $node = $self->{open_elements}->[$_];
6712 wakaba 1.121 if ($node->[1] & TABLE_EL) {
6713 wakaba 1.79
6714 wakaba 1.54 $i = $_;
6715     last INSCOPE;
6716 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6717 wakaba 1.79
6718 wakaba 1.54 last INSCOPE;
6719     }
6720     } # INSCOPE
6721     unless (defined $i) {
6722 wakaba 1.79
6723 wakaba 1.83 ## TODO: The following is wrong, maybe.
6724 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:table', token => $token);
6725 wakaba 1.54 ## Ignore tokens </table><table>
6726 wakaba 1.122
6727 wakaba 1.52 $token = $self->_get_next_token;
6728 wakaba 1.123 next B;
6729 wakaba 1.52 }
6730    
6731 wakaba 1.105 ## TODO: Followings are removed from the latest spec.
6732 wakaba 1.54 ## generate implied end tags
6733 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6734 wakaba 1.79
6735 wakaba 1.86 pop @{$self->{open_elements}};
6736 wakaba 1.54 }
6737    
6738 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
6739 wakaba 1.79
6740 wakaba 1.120 ## NOTE: |<table><tr><table>|
6741     $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
6742     value => $self->{open_elements}->[-1]->[0]
6743     ->manakai_local_name,
6744     token => $token);
6745 wakaba 1.79 } else {
6746    
6747 wakaba 1.54 }
6748    
6749     splice @{$self->{open_elements}}, $i;
6750 wakaba 1.95 pop @{$open_tables};
6751 wakaba 1.54
6752     $self->_reset_insertion_mode;
6753 wakaba 1.52
6754 wakaba 1.122 ## reprocess
6755    
6756 wakaba 1.123 next B;
6757 wakaba 1.100 } elsif ($token->{tag_name} eq 'style') {
6758     if (not $open_tables->[-1]->[1]) { # tainted
6759    
6760     ## NOTE: This is a "as if in head" code clone.
6761     $parse_rcdata->(CDATA_CONTENT_MODEL);
6762 wakaba 1.123 next B;
6763 wakaba 1.100 } else {
6764    
6765     #
6766     }
6767     } elsif ($token->{tag_name} eq 'script') {
6768     if (not $open_tables->[-1]->[1]) { # tainted
6769    
6770     ## NOTE: This is a "as if in head" code clone.
6771     $script_start_tag->();
6772 wakaba 1.123 next B;
6773 wakaba 1.100 } else {
6774    
6775     #
6776     }
6777 wakaba 1.98 } elsif ($token->{tag_name} eq 'input') {
6778     if (not $open_tables->[-1]->[1]) { # tainted
6779     if ($token->{attributes}->{type}) { ## TODO: case
6780     my $type = lc $token->{attributes}->{type}->{value};
6781     if ($type eq 'hidden') {
6782    
6783 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in table:'.$token->{tag_name}, token => $token);
6784 wakaba 1.98
6785    
6786     {
6787     my $el;
6788    
6789     $el = $self->{document}->create_element_ns
6790 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
6791 wakaba 1.98
6792     for my $attr_name (keys %{ $token->{attributes}}) {
6793 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
6794 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
6795 wakaba 1.117 $attr->value ($attr_t->{value});
6796     $attr->set_user_data (manakai_source_line => $attr_t->{line});
6797     $attr->set_user_data (manakai_source_column => $attr_t->{column});
6798     $el->set_attribute_node_ns ($attr);
6799 wakaba 1.98 }
6800    
6801 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
6802     if defined $token->{line};
6803     $el->set_user_data (manakai_source_column => $token->{column})
6804     if defined $token->{column};
6805    
6806 wakaba 1.98 $self->{open_elements}->[-1]->[0]->append_child ($el);
6807 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
6808 wakaba 1.98 }
6809    
6810    
6811     ## TODO: form element pointer
6812    
6813     pop @{$self->{open_elements}};
6814    
6815     $token = $self->_get_next_token;
6816 wakaba 1.122 delete $self->{self_closing};
6817 wakaba 1.123 next B;
6818 wakaba 1.98 } else {
6819    
6820     #
6821     }
6822     } else {
6823    
6824     #
6825     }
6826     } else {
6827    
6828     #
6829     }
6830 wakaba 1.60 } else {
6831 wakaba 1.79
6832 wakaba 1.60 #
6833     }
6834 wakaba 1.98
6835 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in table:'.$token->{tag_name}, token => $token);
6836 wakaba 1.98
6837     $insert = $insert_to_foster;
6838     #
6839 wakaba 1.60 } elsif ($token->{type} == END_TAG_TOKEN) {
6840 wakaba 1.54 if ($token->{tag_name} eq 'tr' and
6841 wakaba 1.56 $self->{insertion_mode} == IN_ROW_IM) {
6842 wakaba 1.54 ## have an element in table scope
6843     my $i;
6844     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6845     my $node = $self->{open_elements}->[$_];
6846 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
6847 wakaba 1.79
6848 wakaba 1.54 $i = $_;
6849     last INSCOPE;
6850 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6851 wakaba 1.79
6852 wakaba 1.54 last INSCOPE;
6853     }
6854     } # INSCOPE
6855     unless (defined $i) {
6856 wakaba 1.79
6857 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6858 wakaba 1.54 ## Ignore the token
6859 wakaba 1.122
6860 wakaba 1.54 $token = $self->_get_next_token;
6861 wakaba 1.123 next B;
6862 wakaba 1.79 } else {
6863    
6864 wakaba 1.54 }
6865    
6866     ## Clear back to table row context
6867 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6868     & TABLE_ROW_SCOPING_EL)) {
6869 wakaba 1.79
6870 wakaba 1.83 ## ISSUE: Can this state be reached?
6871 wakaba 1.54 pop @{$self->{open_elements}};
6872     }
6873    
6874     pop @{$self->{open_elements}}; # tr
6875 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
6876 wakaba 1.54 $token = $self->_get_next_token;
6877 wakaba 1.122
6878 wakaba 1.123 next B;
6879 wakaba 1.54 } elsif ($token->{tag_name} eq 'table') {
6880 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
6881 wakaba 1.54 ## As if </tr>
6882     ## have an element in table scope
6883     my $i;
6884     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6885     my $node = $self->{open_elements}->[$_];
6886 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
6887 wakaba 1.79
6888 wakaba 1.54 $i = $_;
6889     last INSCOPE;
6890 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6891 wakaba 1.79
6892 wakaba 1.54 last INSCOPE;
6893     }
6894     } # INSCOPE
6895     unless (defined $i) {
6896 wakaba 1.79
6897 wakaba 1.83 ## TODO: The following is wrong.
6898 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{type}, token => $token);
6899 wakaba 1.54 ## Ignore the token
6900 wakaba 1.122
6901 wakaba 1.54 $token = $self->_get_next_token;
6902 wakaba 1.123 next B;
6903 wakaba 1.54 }
6904    
6905     ## Clear back to table row context
6906 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6907     & TABLE_ROW_SCOPING_EL)) {
6908 wakaba 1.79
6909 wakaba 1.83 ## ISSUE: Can this state be reached?
6910 wakaba 1.54 pop @{$self->{open_elements}};
6911     }
6912    
6913     pop @{$self->{open_elements}}; # tr
6914 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
6915 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
6916     }
6917    
6918 wakaba 1.56 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6919 wakaba 1.54 ## have an element in table scope
6920     my $i;
6921     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6922     my $node = $self->{open_elements}->[$_];
6923 wakaba 1.121 if ($node->[1] & TABLE_ROW_GROUP_EL) {
6924 wakaba 1.79
6925 wakaba 1.54 $i = $_;
6926     last INSCOPE;
6927 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6928 wakaba 1.79
6929 wakaba 1.54 last INSCOPE;
6930     }
6931     } # INSCOPE
6932     unless (defined $i) {
6933 wakaba 1.79
6934 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6935 wakaba 1.54 ## Ignore the token
6936 wakaba 1.122
6937 wakaba 1.54 $token = $self->_get_next_token;
6938 wakaba 1.123 next B;
6939 wakaba 1.54 }
6940    
6941     ## Clear back to table body context
6942 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
6943     & TABLE_ROWS_SCOPING_EL)) {
6944 wakaba 1.79
6945 wakaba 1.54 pop @{$self->{open_elements}};
6946     }
6947    
6948     ## As if <{current node}>
6949     ## have an element in table scope
6950     ## true by definition
6951    
6952     ## Clear back to table body context
6953     ## nop by definition
6954    
6955     pop @{$self->{open_elements}};
6956 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
6957 wakaba 1.54 ## reprocess in the "in table" insertion mode...
6958     }
6959 wakaba 1.52
6960 wakaba 1.94 ## NOTE: </table> in the "in table" insertion mode.
6961     ## When you edit the code fragment below, please ensure that
6962     ## the code for <table> in the "in table" insertion mode
6963     ## is synced with it.
6964    
6965 wakaba 1.54 ## have a table element in table scope
6966     my $i;
6967     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6968     my $node = $self->{open_elements}->[$_];
6969 wakaba 1.121 if ($node->[1] & TABLE_EL) {
6970 wakaba 1.79
6971 wakaba 1.54 $i = $_;
6972     last INSCOPE;
6973 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
6974 wakaba 1.79
6975 wakaba 1.54 last INSCOPE;
6976     }
6977     } # INSCOPE
6978     unless (defined $i) {
6979 wakaba 1.79
6980 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6981 wakaba 1.54 ## Ignore the token
6982 wakaba 1.122
6983 wakaba 1.54 $token = $self->_get_next_token;
6984 wakaba 1.123 next B;
6985 wakaba 1.51 }
6986 wakaba 1.54
6987     splice @{$self->{open_elements}}, $i;
6988 wakaba 1.95 pop @{$open_tables};
6989 wakaba 1.54
6990     $self->_reset_insertion_mode;
6991 wakaba 1.1
6992     $token = $self->_get_next_token;
6993 wakaba 1.123 next B;
6994 wakaba 1.54 } elsif ({
6995     tbody => 1, tfoot => 1, thead => 1,
6996     }->{$token->{tag_name}} and
6997 wakaba 1.58 $self->{insertion_mode} & ROW_IMS) {
6998 wakaba 1.56 if ($self->{insertion_mode} == IN_ROW_IM) {
6999 wakaba 1.54 ## have an 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->[0]->manakai_local_name eq $token->{tag_name}) {
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.54 }
7020    
7021     ## As if </tr>
7022     ## have an element in table scope
7023     my $i;
7024     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7025     my $node = $self->{open_elements}->[$_];
7026 wakaba 1.121 if ($node->[1] & TABLE_ROW_EL) {
7027 wakaba 1.79
7028 wakaba 1.54 $i = $_;
7029     last INSCOPE;
7030 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7031 wakaba 1.79
7032 wakaba 1.54 last INSCOPE;
7033     }
7034     } # INSCOPE
7035     unless (defined $i) {
7036 wakaba 1.79
7037 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:tr', token => $token);
7038 wakaba 1.54 ## Ignore the token
7039 wakaba 1.122
7040 wakaba 1.54 $token = $self->_get_next_token;
7041 wakaba 1.123 next B;
7042 wakaba 1.54 }
7043    
7044     ## Clear back to table row context
7045 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7046     & TABLE_ROW_SCOPING_EL)) {
7047 wakaba 1.79
7048 wakaba 1.83 ## ISSUE: Can this case be reached?
7049 wakaba 1.54 pop @{$self->{open_elements}};
7050     }
7051    
7052     pop @{$self->{open_elements}}; # tr
7053 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_BODY_IM;
7054 wakaba 1.54 ## reprocess in the "in table body" insertion mode...
7055 wakaba 1.34 }
7056    
7057 wakaba 1.54 ## have an element in table scope
7058     my $i;
7059     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7060     my $node = $self->{open_elements}->[$_];
7061 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7062 wakaba 1.79
7063 wakaba 1.54 $i = $_;
7064     last INSCOPE;
7065 wakaba 1.121 } elsif ($node->[1] & TABLE_SCOPING_EL) {
7066 wakaba 1.79
7067 wakaba 1.54 last INSCOPE;
7068 wakaba 1.34 }
7069 wakaba 1.54 } # INSCOPE
7070     unless (defined $i) {
7071 wakaba 1.79
7072 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
7073 wakaba 1.54 ## Ignore the token
7074 wakaba 1.122
7075 wakaba 1.54 $token = $self->_get_next_token;
7076 wakaba 1.123 next B;
7077 wakaba 1.34 }
7078    
7079 wakaba 1.54 ## Clear back to table body context
7080 wakaba 1.121 while (not ($self->{open_elements}->[-1]->[1]
7081     & TABLE_ROWS_SCOPING_EL)) {
7082 wakaba 1.79
7083 wakaba 1.83 ## ISSUE: Can this case be reached?
7084 wakaba 1.51 pop @{$self->{open_elements}};
7085 wakaba 1.25 }
7086 wakaba 1.51
7087 wakaba 1.54 pop @{$self->{open_elements}};
7088 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7089 wakaba 1.122
7090 wakaba 1.54 $token = $self->_get_next_token;
7091 wakaba 1.123 next B;
7092 wakaba 1.54 } elsif ({
7093     body => 1, caption => 1, col => 1, colgroup => 1,
7094     html => 1, td => 1, th => 1,
7095 wakaba 1.56 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
7096     tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
7097 wakaba 1.54 }->{$token->{tag_name}}) {
7098 wakaba 1.122
7099     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
7100     ## Ignore the token
7101    
7102     $token = $self->_get_next_token;
7103 wakaba 1.123 next B;
7104 wakaba 1.60 } else {
7105 wakaba 1.79
7106 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in table:/'.$token->{tag_name}, token => $token);
7107 wakaba 1.54
7108 wakaba 1.60 $insert = $insert_to_foster;
7109     #
7110     }
7111 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7112 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
7113 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
7114 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in body:#eof', token => $token);
7115 wakaba 1.103
7116 wakaba 1.104 #
7117 wakaba 1.103 } else {
7118    
7119 wakaba 1.104 #
7120 wakaba 1.103 }
7121    
7122     ## Stop parsing
7123     last B;
7124 wakaba 1.60 } else {
7125     die "$0: $token->{type}: Unknown token type";
7126     }
7127 wakaba 1.56 } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7128 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
7129 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
7130     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
7131     unless (length $token->{data}) {
7132 wakaba 1.79
7133 wakaba 1.54 $token = $self->_get_next_token;
7134 wakaba 1.123 next B;
7135 wakaba 1.25 }
7136 wakaba 1.54 }
7137    
7138 wakaba 1.79
7139 wakaba 1.54 #
7140 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
7141 wakaba 1.54 if ($token->{tag_name} eq 'col') {
7142    
7143 wakaba 1.79
7144 wakaba 1.25 {
7145     my $el;
7146    
7147 wakaba 1.1 $el = $self->{document}->create_element_ns
7148 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7149 wakaba 1.1
7150 wakaba 1.25 for my $attr_name (keys %{ $token->{attributes}}) {
7151 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7152 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7153 wakaba 1.117 $attr->value ($attr_t->{value});
7154     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7155     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7156     $el->set_attribute_node_ns ($attr);
7157 wakaba 1.1 }
7158    
7159 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7160     if defined $token->{line};
7161     $el->set_user_data (manakai_source_column => $token->{column})
7162     if defined $token->{column};
7163    
7164 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($el);
7165 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7166 wakaba 1.25 }
7167    
7168 wakaba 1.54 pop @{$self->{open_elements}};
7169 wakaba 1.122 delete $self->{self_closing};
7170 wakaba 1.54 $token = $self->_get_next_token;
7171 wakaba 1.123 next B;
7172 wakaba 1.54 } else {
7173 wakaba 1.79
7174 wakaba 1.54 #
7175     }
7176 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
7177 wakaba 1.54 if ($token->{tag_name} eq 'colgroup') {
7178 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
7179 wakaba 1.79
7180 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:colgroup', token => $token);
7181 wakaba 1.25 ## Ignore the token
7182 wakaba 1.42 $token = $self->_get_next_token;
7183 wakaba 1.123 next B;
7184 wakaba 1.24 } else {
7185 wakaba 1.79
7186 wakaba 1.54 pop @{$self->{open_elements}}; # colgroup
7187 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7188 wakaba 1.54 $token = $self->_get_next_token;
7189 wakaba 1.123 next B;
7190 wakaba 1.25 }
7191 wakaba 1.54 } elsif ($token->{tag_name} eq 'col') {
7192 wakaba 1.79
7193 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:col', token => $token);
7194 wakaba 1.54 ## Ignore the token
7195     $token = $self->_get_next_token;
7196 wakaba 1.123 next B;
7197 wakaba 1.54 } else {
7198 wakaba 1.79
7199 wakaba 1.54 #
7200     }
7201 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7202 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
7203 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
7204    
7205     ## Stop parsing.
7206     last B;
7207     } else {
7208     ## NOTE: As if </colgroup>.
7209    
7210     pop @{$self->{open_elements}}; # colgroup
7211     $self->{insertion_mode} = IN_TABLE_IM;
7212     ## Reprocess.
7213 wakaba 1.123 next B;
7214 wakaba 1.103 }
7215     } else {
7216     die "$0: $token->{type}: Unknown token type";
7217     }
7218 wakaba 1.51
7219 wakaba 1.54 ## As if </colgroup>
7220 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
7221 wakaba 1.79
7222 wakaba 1.103 ## TODO: Wrong error type?
7223 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:colgroup', token => $token);
7224 wakaba 1.54 ## Ignore the token
7225 wakaba 1.122
7226 wakaba 1.54 $token = $self->_get_next_token;
7227 wakaba 1.123 next B;
7228 wakaba 1.54 } else {
7229 wakaba 1.79
7230 wakaba 1.54 pop @{$self->{open_elements}}; # colgroup
7231 wakaba 1.56 $self->{insertion_mode} = IN_TABLE_IM;
7232 wakaba 1.122
7233 wakaba 1.54 ## reprocess
7234 wakaba 1.123 next B;
7235 wakaba 1.54 }
7236 wakaba 1.101 } elsif ($self->{insertion_mode} & SELECT_IMS) {
7237 wakaba 1.60 if ($token->{type} == CHARACTER_TOKEN) {
7238 wakaba 1.79
7239 wakaba 1.60 $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
7240     $token = $self->_get_next_token;
7241 wakaba 1.123 next B;
7242 wakaba 1.60 } elsif ($token->{type} == START_TAG_TOKEN) {
7243 wakaba 1.121 if ($token->{tag_name} eq 'option') {
7244     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
7245    
7246     ## As if </option>
7247     pop @{$self->{open_elements}};
7248     } else {
7249    
7250     }
7251 wakaba 1.51
7252 wakaba 1.121
7253 wakaba 1.1 {
7254     my $el;
7255    
7256     $el = $self->{document}->create_element_ns
7257 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7258 wakaba 1.1
7259     for my $attr_name (keys %{ $token->{attributes}}) {
7260 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7261 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7262 wakaba 1.117 $attr->value ($attr_t->{value});
7263     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7264     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7265     $el->set_attribute_node_ns ($attr);
7266 wakaba 1.1 }
7267    
7268 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7269     if defined $token->{line};
7270     $el->set_user_data (manakai_source_column => $token->{column})
7271     if defined $token->{column};
7272    
7273 wakaba 1.3 $self->{open_elements}->[-1]->[0]->append_child ($el);
7274 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7275 wakaba 1.1 }
7276    
7277 wakaba 1.122
7278 wakaba 1.121 $token = $self->_get_next_token;
7279 wakaba 1.123 next B;
7280 wakaba 1.121 } elsif ($token->{tag_name} eq 'optgroup') {
7281     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
7282    
7283     ## As if </option>
7284     pop @{$self->{open_elements}};
7285     } else {
7286    
7287     }
7288 wakaba 1.54
7289 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
7290    
7291     ## As if </optgroup>
7292     pop @{$self->{open_elements}};
7293     } else {
7294    
7295     }
7296 wakaba 1.51
7297 wakaba 1.121
7298 wakaba 1.1 {
7299     my $el;
7300    
7301     $el = $self->{document}->create_element_ns
7302 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7303 wakaba 1.1
7304 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
7305 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7306 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7307 wakaba 1.117 $attr->value ($attr_t->{value});
7308     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7309     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7310     $el->set_attribute_node_ns ($attr);
7311 wakaba 1.54 }
7312    
7313 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7314     if defined $token->{line};
7315     $el->set_user_data (manakai_source_column => $token->{column})
7316     if defined $token->{column};
7317    
7318 wakaba 1.3 $self->{open_elements}->[-1]->[0]->append_child ($el);
7319 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7320 wakaba 1.1 }
7321    
7322 wakaba 1.122
7323 wakaba 1.121 $token = $self->_get_next_token;
7324 wakaba 1.123 next B;
7325 wakaba 1.101 } elsif ($token->{tag_name} eq 'select' or
7326     $token->{tag_name} eq 'input' or
7327     ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
7328     {
7329     caption => 1, table => 1,
7330     tbody => 1, tfoot => 1, thead => 1,
7331     tr => 1, td => 1, th => 1,
7332     }->{$token->{tag_name}})) {
7333     ## TODO: The type below is not good - <select> is replaced by </select>
7334 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed:select', token => $token);
7335 wakaba 1.101 ## NOTE: As if the token were </select> (<select> case) or
7336     ## as if there were </select> (otherwise).
7337 wakaba 1.121 ## have an element in table scope
7338     my $i;
7339     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7340     my $node = $self->{open_elements}->[$_];
7341     if ($node->[1] & SELECT_EL) {
7342    
7343     $i = $_;
7344     last INSCOPE;
7345     } elsif ($node->[1] & TABLE_SCOPING_EL) {
7346 wakaba 1.54
7347 wakaba 1.121 last INSCOPE;
7348     }
7349     } # INSCOPE
7350     unless (defined $i) {
7351    
7352     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:select', token => $token);
7353     ## Ignore the token
7354 wakaba 1.122
7355 wakaba 1.121 $token = $self->_get_next_token;
7356 wakaba 1.123 next B;
7357 wakaba 1.121 }
7358 wakaba 1.79
7359 wakaba 1.121
7360     splice @{$self->{open_elements}}, $i;
7361 wakaba 1.54
7362 wakaba 1.121 $self->_reset_insertion_mode;
7363 wakaba 1.54
7364 wakaba 1.101 if ($token->{tag_name} eq 'select') {
7365    
7366     $token = $self->_get_next_token;
7367 wakaba 1.123 next B;
7368 wakaba 1.101 } else {
7369    
7370 wakaba 1.122
7371 wakaba 1.101 ## Reprocess the token.
7372 wakaba 1.123 next B;
7373 wakaba 1.101 }
7374 wakaba 1.60 } else {
7375 wakaba 1.79
7376 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in select:'.$token->{tag_name}, token => $token);
7377 wakaba 1.60 ## Ignore the token
7378 wakaba 1.122
7379 wakaba 1.60 $token = $self->_get_next_token;
7380 wakaba 1.123 next B;
7381 wakaba 1.60 }
7382     } elsif ($token->{type} == END_TAG_TOKEN) {
7383 wakaba 1.121 if ($token->{tag_name} eq 'optgroup') {
7384     if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
7385     $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
7386    
7387     ## As if </option>
7388     splice @{$self->{open_elements}}, -2;
7389     } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
7390    
7391     pop @{$self->{open_elements}};
7392     } else {
7393    
7394     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
7395     ## Ignore the token
7396     }
7397 wakaba 1.122
7398 wakaba 1.121 $token = $self->_get_next_token;
7399 wakaba 1.123 next B;
7400 wakaba 1.121 } elsif ($token->{tag_name} eq 'option') {
7401     if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
7402    
7403     pop @{$self->{open_elements}};
7404     } else {
7405    
7406     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
7407     ## Ignore the token
7408     }
7409 wakaba 1.122
7410 wakaba 1.121 $token = $self->_get_next_token;
7411 wakaba 1.123 next B;
7412 wakaba 1.121 } elsif ($token->{tag_name} eq 'select') {
7413     ## have an element in table scope
7414     my $i;
7415     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7416     my $node = $self->{open_elements}->[$_];
7417     if ($node->[1] & SELECT_EL) {
7418    
7419     $i = $_;
7420     last INSCOPE;
7421     } elsif ($node->[1] & TABLE_SCOPING_EL) {
7422 wakaba 1.54
7423 wakaba 1.121 last INSCOPE;
7424     }
7425     } # INSCOPE
7426     unless (defined $i) {
7427    
7428     $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
7429     ## Ignore the token
7430 wakaba 1.122
7431 wakaba 1.121 $token = $self->_get_next_token;
7432 wakaba 1.123 next B;
7433 wakaba 1.121 }
7434 wakaba 1.79
7435 wakaba 1.121
7436     splice @{$self->{open_elements}}, $i;
7437 wakaba 1.54
7438 wakaba 1.121 $self->_reset_insertion_mode;
7439 wakaba 1.54
7440 wakaba 1.122
7441 wakaba 1.121 $token = $self->_get_next_token;
7442 wakaba 1.123 next B;
7443 wakaba 1.101 } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
7444     {
7445     caption => 1, table => 1, tbody => 1,
7446     tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
7447     }->{$token->{tag_name}}) {
7448 wakaba 1.83 ## TODO: The following is wrong?
7449 wakaba 1.121 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
7450    
7451     ## have an element in table scope
7452     my $i;
7453     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7454     my $node = $self->{open_elements}->[$_];
7455     if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7456    
7457     $i = $_;
7458     last INSCOPE;
7459     } elsif ($node->[1] & TABLE_SCOPING_EL) {
7460    
7461     last INSCOPE;
7462     }
7463     } # INSCOPE
7464     unless (defined $i) {
7465    
7466     ## Ignore the token
7467 wakaba 1.122
7468 wakaba 1.121 $token = $self->_get_next_token;
7469 wakaba 1.123 next B;
7470 wakaba 1.121 }
7471 wakaba 1.54
7472 wakaba 1.121 ## As if </select>
7473     ## have an element in table scope
7474     undef $i;
7475     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7476     my $node = $self->{open_elements}->[$_];
7477     if ($node->[1] & SELECT_EL) {
7478 wakaba 1.54
7479 wakaba 1.121 $i = $_;
7480     last INSCOPE;
7481     } elsif ($node->[1] & TABLE_SCOPING_EL) {
7482 wakaba 1.83 ## ISSUE: Can this state be reached?
7483 wakaba 1.121
7484     last INSCOPE;
7485     }
7486     } # INSCOPE
7487     unless (defined $i) {
7488    
7489 wakaba 1.83 ## TODO: The following error type is correct?
7490 wakaba 1.121 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:select', token => $token);
7491     ## Ignore the </select> token
7492 wakaba 1.122
7493 wakaba 1.121 $token = $self->_get_next_token; ## TODO: ok?
7494 wakaba 1.123 next B;
7495 wakaba 1.121 }
7496 wakaba 1.54
7497 wakaba 1.121
7498     splice @{$self->{open_elements}}, $i;
7499 wakaba 1.54
7500 wakaba 1.121 $self->_reset_insertion_mode;
7501 wakaba 1.54
7502 wakaba 1.122
7503 wakaba 1.121 ## reprocess
7504 wakaba 1.123 next B;
7505 wakaba 1.60 } else {
7506 wakaba 1.79
7507 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in select:/'.$token->{tag_name}, token => $token);
7508 wakaba 1.54 ## Ignore the token
7509 wakaba 1.122
7510 wakaba 1.54 $token = $self->_get_next_token;
7511 wakaba 1.123 next B;
7512 wakaba 1.60 }
7513 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7514 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
7515 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
7516    
7517 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in body:#eof', token => $token);
7518 wakaba 1.103 } else {
7519    
7520     }
7521    
7522     ## Stop parsing.
7523     last B;
7524 wakaba 1.60 } else {
7525     die "$0: $token->{type}: Unknown token type";
7526     }
7527 wakaba 1.58 } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
7528 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
7529 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
7530     my $data = $1;
7531     ## As if in body
7532     $reconstruct_active_formatting_elements->($insert_to_current);
7533    
7534     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
7535    
7536     unless (length $token->{data}) {
7537 wakaba 1.79
7538 wakaba 1.54 $token = $self->_get_next_token;
7539 wakaba 1.123 next B;
7540 wakaba 1.54 }
7541     }
7542    
7543 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
7544 wakaba 1.79
7545 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after html:#character', token => $token);
7546 wakaba 1.54
7547 wakaba 1.85 ## Reprocess in the "after body" insertion mode.
7548 wakaba 1.79 } else {
7549    
7550 wakaba 1.54 }
7551    
7552     ## "after body" insertion mode
7553 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after body:#character', token => $token);
7554 wakaba 1.54
7555 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
7556 wakaba 1.54 ## reprocess
7557 wakaba 1.123 next B;
7558 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
7559 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
7560 wakaba 1.79
7561 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after html:'.$token->{tag_name}, token => $token);
7562 wakaba 1.54
7563 wakaba 1.85 ## Reprocess in the "after body" insertion mode.
7564 wakaba 1.79 } else {
7565    
7566 wakaba 1.54 }
7567    
7568     ## "after body" insertion mode
7569 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after body:'.$token->{tag_name}, token => $token);
7570 wakaba 1.54
7571 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
7572 wakaba 1.122
7573 wakaba 1.54 ## reprocess
7574 wakaba 1.123 next B;
7575 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
7576 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
7577 wakaba 1.79
7578 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after html:/'.$token->{tag_name}, token => $token);
7579 wakaba 1.54
7580 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
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     if ($token->{tag_name} eq 'html') {
7588     if (defined $self->{inner_html_node}) {
7589 wakaba 1.79
7590 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:html', token => $token);
7591 wakaba 1.54 ## Ignore the token
7592     $token = $self->_get_next_token;
7593 wakaba 1.123 next B;
7594 wakaba 1.54 } else {
7595 wakaba 1.79
7596 wakaba 1.56 $self->{insertion_mode} = AFTER_HTML_BODY_IM;
7597 wakaba 1.54 $token = $self->_get_next_token;
7598 wakaba 1.123 next B;
7599 wakaba 1.54 }
7600     } else {
7601 wakaba 1.79
7602 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after body:/'.$token->{tag_name}, token => $token);
7603 wakaba 1.54
7604 wakaba 1.56 $self->{insertion_mode} = IN_BODY_IM;
7605 wakaba 1.54 ## reprocess
7606 wakaba 1.123 next B;
7607 wakaba 1.54 }
7608 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7609    
7610     ## Stop parsing
7611     last B;
7612 wakaba 1.54 } else {
7613     die "$0: $token->{type}: Unknown token type";
7614     }
7615 wakaba 1.58 } elsif ($self->{insertion_mode} & FRAME_IMS) {
7616 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
7617 wakaba 1.54 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
7618     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
7619    
7620     unless (length $token->{data}) {
7621 wakaba 1.79
7622 wakaba 1.54 $token = $self->_get_next_token;
7623 wakaba 1.123 next B;
7624 wakaba 1.54 }
7625     }
7626    
7627     if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
7628 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
7629 wakaba 1.79
7630 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in frameset:#character', token => $token);
7631 wakaba 1.56 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
7632 wakaba 1.79
7633 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after frameset:#character', token => $token);
7634 wakaba 1.54 } else { # "after html frameset"
7635 wakaba 1.79
7636 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after html:#character', token => $token);
7637 wakaba 1.54
7638 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
7639 wakaba 1.85 ## Reprocess in the "after frameset" insertion mode.
7640 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after frameset:#character', token => $token);
7641 wakaba 1.54 }
7642    
7643     ## Ignore the token.
7644     if (length $token->{data}) {
7645 wakaba 1.79
7646 wakaba 1.54 ## reprocess the rest of characters
7647     } else {
7648 wakaba 1.79
7649 wakaba 1.54 $token = $self->_get_next_token;
7650     }
7651 wakaba 1.123 next B;
7652 wakaba 1.54 }
7653    
7654     die qq[$0: Character "$token->{data}"];
7655 wakaba 1.57 } elsif ($token->{type} == START_TAG_TOKEN) {
7656 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
7657 wakaba 1.79
7658 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after html:'.$token->{tag_name}, token => $token);
7659 wakaba 1.54
7660 wakaba 1.79 $self->{insertion_mode} = AFTER_FRAMESET_IM;
7661 wakaba 1.85 ## Process in the "after frameset" insertion mode.
7662 wakaba 1.79 } else {
7663    
7664     }
7665    
7666 wakaba 1.54 if ($token->{tag_name} eq 'frameset' and
7667 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
7668 wakaba 1.54
7669 wakaba 1.79
7670 wakaba 1.54 {
7671     my $el;
7672    
7673     $el = $self->{document}->create_element_ns
7674 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7675 wakaba 1.54
7676     for my $attr_name (keys %{ $token->{attributes}}) {
7677 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7678 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7679 wakaba 1.117 $attr->value ($attr_t->{value});
7680     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7681     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7682     $el->set_attribute_node_ns ($attr);
7683 wakaba 1.54 }
7684    
7685 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7686     if defined $token->{line};
7687     $el->set_user_data (manakai_source_column => $token->{column})
7688     if defined $token->{column};
7689    
7690 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7691 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7692 wakaba 1.54 }
7693    
7694 wakaba 1.122
7695 wakaba 1.54 $token = $self->_get_next_token;
7696 wakaba 1.123 next B;
7697 wakaba 1.54 } elsif ($token->{tag_name} eq 'frame' and
7698 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
7699 wakaba 1.54
7700 wakaba 1.79
7701 wakaba 1.54 {
7702     my $el;
7703    
7704     $el = $self->{document}->create_element_ns
7705 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7706 wakaba 1.54
7707     for my $attr_name (keys %{ $token->{attributes}}) {
7708 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7709 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7710 wakaba 1.117 $attr->value ($attr_t->{value});
7711     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7712     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7713     $el->set_attribute_node_ns ($attr);
7714 wakaba 1.54 }
7715    
7716 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7717     if defined $token->{line};
7718     $el->set_user_data (manakai_source_column => $token->{column})
7719     if defined $token->{column};
7720    
7721 wakaba 1.54 $self->{open_elements}->[-1]->[0]->append_child ($el);
7722 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7723 wakaba 1.54 }
7724    
7725     pop @{$self->{open_elements}};
7726 wakaba 1.122 delete $self->{self_closing};
7727 wakaba 1.54 $token = $self->_get_next_token;
7728 wakaba 1.123 next B;
7729 wakaba 1.54 } elsif ($token->{tag_name} eq 'noframes') {
7730 wakaba 1.79
7731 wakaba 1.54 ## NOTE: As if in body.
7732 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
7733 wakaba 1.123 next B;
7734 wakaba 1.54 } else {
7735 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
7736 wakaba 1.79
7737 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in frameset:'.$token->{tag_name}, token => $token);
7738 wakaba 1.54 } else {
7739 wakaba 1.79
7740 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after frameset:'.$token->{tag_name}, token => $token);
7741 wakaba 1.54 }
7742     ## Ignore the token
7743 wakaba 1.122
7744 wakaba 1.54 $token = $self->_get_next_token;
7745 wakaba 1.123 next B;
7746 wakaba 1.54 }
7747 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
7748 wakaba 1.56 if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
7749 wakaba 1.79
7750 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after html:/'.$token->{tag_name}, token => $token);
7751 wakaba 1.54
7752 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
7753 wakaba 1.85 ## Process in the "after frameset" insertion mode.
7754 wakaba 1.79 } else {
7755    
7756 wakaba 1.54 }
7757    
7758     if ($token->{tag_name} eq 'frameset' and
7759 wakaba 1.56 $self->{insertion_mode} == IN_FRAMESET_IM) {
7760 wakaba 1.121 if ($self->{open_elements}->[-1]->[1] & HTML_EL and
7761 wakaba 1.54 @{$self->{open_elements}} == 1) {
7762 wakaba 1.79
7763 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
7764 wakaba 1.54 ## Ignore the token
7765     $token = $self->_get_next_token;
7766     } else {
7767 wakaba 1.79
7768 wakaba 1.54 pop @{$self->{open_elements}};
7769     $token = $self->_get_next_token;
7770     }
7771 wakaba 1.43
7772 wakaba 1.54 if (not defined $self->{inner_html_node} and
7773 wakaba 1.121 not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
7774 wakaba 1.79
7775 wakaba 1.56 $self->{insertion_mode} = AFTER_FRAMESET_IM;
7776 wakaba 1.79 } else {
7777    
7778 wakaba 1.54 }
7779 wakaba 1.123 next B;
7780 wakaba 1.54 } elsif ($token->{tag_name} eq 'html' and
7781 wakaba 1.56 $self->{insertion_mode} == AFTER_FRAMESET_IM) {
7782 wakaba 1.79
7783 wakaba 1.56 $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
7784 wakaba 1.54 $token = $self->_get_next_token;
7785 wakaba 1.123 next B;
7786 wakaba 1.54 } else {
7787 wakaba 1.56 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
7788 wakaba 1.79
7789 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in frameset:/'.$token->{tag_name}, token => $token);
7790 wakaba 1.54 } else {
7791 wakaba 1.79
7792 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'after frameset:/'.$token->{tag_name}, token => $token);
7793 wakaba 1.54 }
7794     ## Ignore the token
7795     $token = $self->_get_next_token;
7796 wakaba 1.123 next B;
7797 wakaba 1.54 }
7798 wakaba 1.103 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
7799 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
7800 wakaba 1.103 @{$self->{open_elements}} == 1) { # redundant, maybe
7801    
7802 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in body:#eof', token => $token);
7803 wakaba 1.103 } else {
7804    
7805     }
7806    
7807     ## Stop parsing
7808     last B;
7809 wakaba 1.54 } else {
7810     die "$0: $token->{type}: Unknown token type";
7811     }
7812 wakaba 1.43
7813 wakaba 1.54 ## ISSUE: An issue in spec here
7814     } else {
7815     die "$0: $self->{insertion_mode}: Unknown insertion mode";
7816     }
7817 wakaba 1.43
7818 wakaba 1.54 ## "in body" insertion mode
7819 wakaba 1.57 if ($token->{type} == START_TAG_TOKEN) {
7820 wakaba 1.54 if ($token->{tag_name} eq 'script') {
7821 wakaba 1.79
7822 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
7823 wakaba 1.100 $script_start_tag->();
7824 wakaba 1.123 next B;
7825 wakaba 1.54 } elsif ($token->{tag_name} eq 'style') {
7826 wakaba 1.79
7827 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
7828 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
7829 wakaba 1.123 next B;
7830 wakaba 1.54 } elsif ({
7831     base => 1, link => 1,
7832     }->{$token->{tag_name}}) {
7833 wakaba 1.79
7834 wakaba 1.54 ## NOTE: This is an "as if in head" code clone, only "-t" differs
7835    
7836     {
7837     my $el;
7838    
7839     $el = $self->{document}->create_element_ns
7840 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7841 wakaba 1.54
7842     for my $attr_name (keys %{ $token->{attributes}}) {
7843 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7844 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7845 wakaba 1.117 $attr->value ($attr_t->{value});
7846     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7847     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7848     $el->set_attribute_node_ns ($attr);
7849 wakaba 1.54 }
7850    
7851 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7852     if defined $token->{line};
7853     $el->set_user_data (manakai_source_column => $token->{column})
7854     if defined $token->{column};
7855    
7856 wakaba 1.54 $insert->($el);
7857 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7858 wakaba 1.54 }
7859    
7860     pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
7861 wakaba 1.122 delete $self->{self_closing};
7862 wakaba 1.54 $token = $self->_get_next_token;
7863 wakaba 1.123 next B;
7864 wakaba 1.54 } elsif ($token->{tag_name} eq 'meta') {
7865     ## NOTE: This is an "as if in head" code clone, only "-t" differs
7866    
7867     {
7868     my $el;
7869    
7870     $el = $self->{document}->create_element_ns
7871 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
7872 wakaba 1.54
7873     for my $attr_name (keys %{ $token->{attributes}}) {
7874 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
7875 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
7876 wakaba 1.117 $attr->value ($attr_t->{value});
7877     $attr->set_user_data (manakai_source_line => $attr_t->{line});
7878     $attr->set_user_data (manakai_source_column => $attr_t->{column});
7879     $el->set_attribute_node_ns ($attr);
7880 wakaba 1.54 }
7881    
7882 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
7883     if defined $token->{line};
7884     $el->set_user_data (manakai_source_column => $token->{column})
7885     if defined $token->{column};
7886    
7887 wakaba 1.54 $insert->($el);
7888 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
7889 wakaba 1.54 }
7890    
7891 wakaba 1.68 my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
7892 wakaba 1.43
7893 wakaba 1.54 unless ($self->{confident}) {
7894 wakaba 1.131 if ($token->{attributes}->{charset}) {
7895 wakaba 1.79
7896 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
7897     ## in the {change_encoding} callback.
7898 wakaba 1.65 $self->{change_encoding}
7899 wakaba 1.112 ->($self, $token->{attributes}->{charset}->{value}, $token);
7900 wakaba 1.68
7901     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
7902     ->set_user_data (manakai_has_reference =>
7903     $token->{attributes}->{charset}
7904     ->{has_reference});
7905 wakaba 1.65 } elsif ($token->{attributes}->{content}) {
7906     if ($token->{attributes}->{content}->{value}
7907 wakaba 1.71 =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
7908     [\x09-\x0D\x20]*=
7909 wakaba 1.54 [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
7910     ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
7911 wakaba 1.79
7912 wakaba 1.131 ## NOTE: Whether the encoding is supported or not is handled
7913     ## in the {change_encoding} callback.
7914 wakaba 1.65 $self->{change_encoding}
7915 wakaba 1.112 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
7916 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
7917     ->set_user_data (manakai_has_reference =>
7918     $token->{attributes}->{content}
7919     ->{has_reference});
7920 wakaba 1.65 }
7921 wakaba 1.54 }
7922 wakaba 1.68 } else {
7923     if ($token->{attributes}->{charset}) {
7924 wakaba 1.79
7925 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
7926     ->set_user_data (manakai_has_reference =>
7927     $token->{attributes}->{charset}
7928     ->{has_reference});
7929     }
7930 wakaba 1.69 if ($token->{attributes}->{content}) {
7931 wakaba 1.79
7932 wakaba 1.69 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
7933     ->set_user_data (manakai_has_reference =>
7934     $token->{attributes}->{content}
7935     ->{has_reference});
7936     }
7937 wakaba 1.54 }
7938 wakaba 1.43
7939 wakaba 1.122 delete $self->{self_closing};
7940 wakaba 1.54 $token = $self->_get_next_token;
7941 wakaba 1.123 next B;
7942 wakaba 1.54 } elsif ($token->{tag_name} eq 'title') {
7943 wakaba 1.79
7944 wakaba 1.54 ## NOTE: This is an "as if in head" code clone
7945 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
7946 wakaba 1.123 next B;
7947 wakaba 1.54 } elsif ($token->{tag_name} eq 'body') {
7948 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in body:body', token => $token);
7949 wakaba 1.1
7950 wakaba 1.54 if (@{$self->{open_elements}} == 1 or
7951 wakaba 1.121 not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
7952 wakaba 1.79
7953 wakaba 1.54 ## Ignore the token
7954     } else {
7955     my $body_el = $self->{open_elements}->[1]->[0];
7956     for my $attr_name (keys %{$token->{attributes}}) {
7957     unless ($body_el->has_attribute_ns (undef, $attr_name)) {
7958 wakaba 1.79
7959 wakaba 1.54 $body_el->set_attribute_ns
7960     (undef, [undef, $attr_name],
7961     $token->{attributes}->{$attr_name}->{value});
7962 wakaba 1.1 }
7963 wakaba 1.54 }
7964     }
7965 wakaba 1.122
7966 wakaba 1.54 $token = $self->_get_next_token;
7967 wakaba 1.123 next B;
7968 wakaba 1.54 } elsif ({
7969     address => 1, blockquote => 1, center => 1, dir => 1,
7970 wakaba 1.85 div => 1, dl => 1, fieldset => 1,
7971     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7972 wakaba 1.97 menu => 1, ol => 1, p => 1, ul => 1,
7973     pre => 1, listing => 1,
7974 wakaba 1.107 form => 1,
7975     table => 1,
7976     hr => 1,
7977 wakaba 1.54 }->{$token->{tag_name}}) {
7978 wakaba 1.107 if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
7979    
7980 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in form:form', token => $token);
7981 wakaba 1.107 ## Ignore the token
7982 wakaba 1.122
7983 wakaba 1.107 $token = $self->_get_next_token;
7984 wakaba 1.123 next B;
7985 wakaba 1.107 }
7986    
7987 wakaba 1.54 ## has a p element in scope
7988     INSCOPE: for (reverse @{$self->{open_elements}}) {
7989 wakaba 1.121 if ($_->[1] & P_EL) {
7990 wakaba 1.79
7991 wakaba 1.122
7992     $token->{self_closing} = $self->{self_closing};
7993     unshift @{$self->{token}}, $token;
7994     delete $self->{self_closing};
7995     # <form>
7996 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
7997     line => $token->{line}, column => $token->{column}};
7998 wakaba 1.123 next B;
7999 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
8000 wakaba 1.79
8001 wakaba 1.54 last INSCOPE;
8002     }
8003     } # INSCOPE
8004    
8005    
8006 wakaba 1.48 {
8007     my $el;
8008    
8009     $el = $self->{document}->create_element_ns
8010 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8011 wakaba 1.48
8012 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
8013 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8014 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8015 wakaba 1.117 $attr->value ($attr_t->{value});
8016     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8017     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8018     $el->set_attribute_node_ns ($attr);
8019 wakaba 1.54 }
8020    
8021 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8022     if defined $token->{line};
8023     $el->set_user_data (manakai_source_column => $token->{column})
8024     if defined $token->{column};
8025    
8026 wakaba 1.54 $insert->($el);
8027 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8028 wakaba 1.48 }
8029    
8030 wakaba 1.97 if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
8031 wakaba 1.122
8032 wakaba 1.54 $token = $self->_get_next_token;
8033 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8034 wakaba 1.54 $token->{data} =~ s/^\x0A//;
8035     unless (length $token->{data}) {
8036 wakaba 1.79
8037 wakaba 1.54 $token = $self->_get_next_token;
8038 wakaba 1.79 } else {
8039    
8040 wakaba 1.54 }
8041 wakaba 1.79 } else {
8042    
8043 wakaba 1.54 }
8044 wakaba 1.107 } elsif ($token->{tag_name} eq 'form') {
8045    
8046     $self->{form_element} = $self->{open_elements}->[-1]->[0];
8047    
8048 wakaba 1.122
8049 wakaba 1.107 $token = $self->_get_next_token;
8050     } elsif ($token->{tag_name} eq 'table') {
8051    
8052     push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
8053 wakaba 1.79
8054 wakaba 1.107 $self->{insertion_mode} = IN_TABLE_IM;
8055    
8056 wakaba 1.122
8057 wakaba 1.54 $token = $self->_get_next_token;
8058 wakaba 1.107 } elsif ($token->{tag_name} eq 'hr') {
8059 wakaba 1.79
8060 wakaba 1.107 pop @{$self->{open_elements}};
8061    
8062 wakaba 1.122
8063 wakaba 1.54 $token = $self->_get_next_token;
8064     } else {
8065    
8066     $token = $self->_get_next_token;
8067     }
8068 wakaba 1.123 next B;
8069 wakaba 1.107 } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
8070 wakaba 1.54 ## has a p element in scope
8071     INSCOPE: for (reverse @{$self->{open_elements}}) {
8072 wakaba 1.121 if ($_->[1] & P_EL) {
8073 wakaba 1.79
8074 wakaba 1.122
8075     $token->{self_closing} = $self->{self_closing};
8076     unshift @{$self->{token}}, $token;
8077     delete $self->{self_closing};
8078     # <x>
8079 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
8080     line => $token->{line}, column => $token->{column}};
8081 wakaba 1.123 next B;
8082 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
8083 wakaba 1.79
8084 wakaba 1.54 last INSCOPE;
8085     }
8086     } # INSCOPE
8087    
8088     ## Step 1
8089     my $i = -1;
8090     my $node = $self->{open_elements}->[$i];
8091 wakaba 1.107 my $li_or_dtdd = {li => {li => 1},
8092     dt => {dt => 1, dd => 1},
8093     dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
8094 wakaba 1.54 LI: {
8095     ## Step 2
8096 wakaba 1.121 if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
8097 wakaba 1.54 if ($i != -1) {
8098 wakaba 1.79
8099 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
8100     value => $self->{open_elements}->[-1]->[0]
8101     ->manakai_local_name,
8102     token => $token);
8103 wakaba 1.79 } else {
8104    
8105 wakaba 1.54 }
8106     splice @{$self->{open_elements}}, $i;
8107     last LI;
8108 wakaba 1.79 } else {
8109    
8110 wakaba 1.54 }
8111    
8112     ## Step 3
8113 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
8114 wakaba 1.54 #not $phrasing_category->{$node->[1]} and
8115 wakaba 1.121 ($node->[1] & SPECIAL_EL or
8116     $node->[1] & SCOPING_EL) and
8117     not ($node->[1] & ADDRESS_EL) and
8118     not ($node->[1] & DIV_EL)) {
8119 wakaba 1.79
8120 wakaba 1.54 last LI;
8121     }
8122    
8123 wakaba 1.79
8124 wakaba 1.54 ## Step 4
8125     $i--;
8126     $node = $self->{open_elements}->[$i];
8127     redo LI;
8128     } # LI
8129    
8130    
8131 wakaba 1.49 {
8132     my $el;
8133    
8134     $el = $self->{document}->create_element_ns
8135 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8136 wakaba 1.49
8137     for my $attr_name (keys %{ $token->{attributes}}) {
8138 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8139 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8140 wakaba 1.117 $attr->value ($attr_t->{value});
8141     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8142     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8143     $el->set_attribute_node_ns ($attr);
8144 wakaba 1.49 }
8145    
8146 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8147     if defined $token->{line};
8148     $el->set_user_data (manakai_source_column => $token->{column})
8149     if defined $token->{column};
8150    
8151 wakaba 1.54 $insert->($el);
8152 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8153 wakaba 1.49 }
8154    
8155 wakaba 1.122
8156 wakaba 1.54 $token = $self->_get_next_token;
8157 wakaba 1.123 next B;
8158 wakaba 1.54 } elsif ($token->{tag_name} eq 'plaintext') {
8159     ## has a p element in scope
8160     INSCOPE: for (reverse @{$self->{open_elements}}) {
8161 wakaba 1.121 if ($_->[1] & P_EL) {
8162 wakaba 1.79
8163 wakaba 1.122
8164     $token->{self_closing} = $self->{self_closing};
8165     unshift @{$self->{token}}, $token;
8166     delete $self->{self_closing};
8167     # <plaintext>
8168 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'p',
8169     line => $token->{line}, column => $token->{column}};
8170 wakaba 1.123 next B;
8171 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
8172 wakaba 1.79
8173 wakaba 1.54 last INSCOPE;
8174     }
8175     } # INSCOPE
8176    
8177    
8178 wakaba 1.1 {
8179     my $el;
8180    
8181     $el = $self->{document}->create_element_ns
8182 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8183 wakaba 1.1
8184 wakaba 1.54 for my $attr_name (keys %{ $token->{attributes}}) {
8185 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8186 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8187 wakaba 1.117 $attr->value ($attr_t->{value});
8188     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8189     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8190     $el->set_attribute_node_ns ($attr);
8191 wakaba 1.54 }
8192    
8193 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8194     if defined $token->{line};
8195     $el->set_user_data (manakai_source_column => $token->{column})
8196     if defined $token->{column};
8197    
8198 wakaba 1.54 $insert->($el);
8199 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8200 wakaba 1.1 }
8201    
8202 wakaba 1.54
8203     $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
8204    
8205 wakaba 1.122
8206 wakaba 1.54 $token = $self->_get_next_token;
8207 wakaba 1.123 next B;
8208 wakaba 1.54 } elsif ($token->{tag_name} eq 'a') {
8209     AFE: for my $i (reverse 0..$#$active_formatting_elements) {
8210     my $node = $active_formatting_elements->[$i];
8211 wakaba 1.121 if ($node->[1] & A_EL) {
8212 wakaba 1.79
8213 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in a:a', token => $token);
8214 wakaba 1.54
8215 wakaba 1.122
8216     $token->{self_closing} = $self->{self_closing};
8217     unshift @{$self->{token}}, $token;
8218     delete $self->{self_closing};
8219     # <a>
8220 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'a',
8221     line => $token->{line}, column => $token->{column}};
8222 wakaba 1.111 $formatting_end_tag->($token);
8223 wakaba 1.54
8224     AFE2: for (reverse 0..$#$active_formatting_elements) {
8225     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
8226 wakaba 1.79
8227 wakaba 1.54 splice @$active_formatting_elements, $_, 1;
8228     last AFE2;
8229 wakaba 1.49 }
8230 wakaba 1.54 } # AFE2
8231     OE: for (reverse 0..$#{$self->{open_elements}}) {
8232     if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
8233 wakaba 1.79
8234 wakaba 1.54 splice @{$self->{open_elements}}, $_, 1;
8235     last OE;
8236 wakaba 1.49 }
8237 wakaba 1.54 } # OE
8238     last AFE;
8239     } elsif ($node->[0] eq '#marker') {
8240 wakaba 1.79
8241 wakaba 1.54 last AFE;
8242     }
8243     } # AFE
8244    
8245     $reconstruct_active_formatting_elements->($insert_to_current);
8246 wakaba 1.49
8247 wakaba 1.54
8248     {
8249     my $el;
8250    
8251     $el = $self->{document}->create_element_ns
8252 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8253 wakaba 1.54
8254     for my $attr_name (keys %{ $token->{attributes}}) {
8255 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8256 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8257 wakaba 1.117 $attr->value ($attr_t->{value});
8258     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8259     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8260     $el->set_attribute_node_ns ($attr);
8261 wakaba 1.54 }
8262    
8263 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8264     if defined $token->{line};
8265     $el->set_user_data (manakai_source_column => $token->{column})
8266     if defined $token->{column};
8267    
8268 wakaba 1.54 $insert->($el);
8269 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8270 wakaba 1.54 }
8271    
8272     push @$active_formatting_elements, $self->{open_elements}->[-1];
8273 wakaba 1.48
8274 wakaba 1.122
8275 wakaba 1.54 $token = $self->_get_next_token;
8276 wakaba 1.123 next B;
8277 wakaba 1.54 } elsif ($token->{tag_name} eq 'nobr') {
8278     $reconstruct_active_formatting_elements->($insert_to_current);
8279 wakaba 1.1
8280 wakaba 1.54 ## has a |nobr| element in scope
8281     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8282     my $node = $self->{open_elements}->[$_];
8283 wakaba 1.121 if ($node->[1] & NOBR_EL) {
8284 wakaba 1.79
8285 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in nobr:nobr', token => $token);
8286 wakaba 1.122
8287     $token->{self_closing} = $self->{self_closing};
8288     unshift @{$self->{token}}, $token;
8289     delete $self->{self_closing};
8290     # <nobr>
8291 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
8292     line => $token->{line}, column => $token->{column}};
8293 wakaba 1.123 next B;
8294 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
8295 wakaba 1.79
8296 wakaba 1.54 last INSCOPE;
8297     }
8298     } # INSCOPE
8299    
8300    
8301     {
8302     my $el;
8303    
8304     $el = $self->{document}->create_element_ns
8305 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8306 wakaba 1.54
8307     for my $attr_name (keys %{ $token->{attributes}}) {
8308 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8309 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8310 wakaba 1.117 $attr->value ($attr_t->{value});
8311     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8312     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8313     $el->set_attribute_node_ns ($attr);
8314 wakaba 1.54 }
8315    
8316 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8317     if defined $token->{line};
8318     $el->set_user_data (manakai_source_column => $token->{column})
8319     if defined $token->{column};
8320    
8321 wakaba 1.54 $insert->($el);
8322 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8323 wakaba 1.54 }
8324    
8325     push @$active_formatting_elements, $self->{open_elements}->[-1];
8326    
8327 wakaba 1.122
8328 wakaba 1.54 $token = $self->_get_next_token;
8329 wakaba 1.123 next B;
8330 wakaba 1.54 } elsif ($token->{tag_name} eq 'button') {
8331     ## has a button element in scope
8332     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8333     my $node = $self->{open_elements}->[$_];
8334 wakaba 1.121 if ($node->[1] & BUTTON_EL) {
8335 wakaba 1.79
8336 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in button:button', token => $token);
8337 wakaba 1.122
8338     $token->{self_closing} = $self->{self_closing};
8339     unshift @{$self->{token}}, $token;
8340     delete $self->{self_closing};
8341     # <button>
8342 wakaba 1.112 $token = {type => END_TAG_TOKEN, tag_name => 'button',
8343     line => $token->{line}, column => $token->{column}};
8344 wakaba 1.123 next B;
8345 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
8346 wakaba 1.79
8347 wakaba 1.54 last INSCOPE;
8348 wakaba 1.1 }
8349 wakaba 1.54 } # INSCOPE
8350    
8351     $reconstruct_active_formatting_elements->($insert_to_current);
8352    
8353    
8354     {
8355     my $el;
8356    
8357     $el = $self->{document}->create_element_ns
8358 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8359 wakaba 1.54
8360     for my $attr_name (keys %{ $token->{attributes}}) {
8361 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8362 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8363 wakaba 1.117 $attr->value ($attr_t->{value});
8364     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8365     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8366     $el->set_attribute_node_ns ($attr);
8367 wakaba 1.54 }
8368    
8369 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8370     if defined $token->{line};
8371     $el->set_user_data (manakai_source_column => $token->{column})
8372     if defined $token->{column};
8373    
8374 wakaba 1.54 $insert->($el);
8375 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8376 wakaba 1.54 }
8377    
8378 wakaba 1.86
8379     ## TODO: associate with $self->{form_element} if defined
8380    
8381 wakaba 1.54 push @$active_formatting_elements, ['#marker', ''];
8382 wakaba 1.1
8383 wakaba 1.122
8384 wakaba 1.54 $token = $self->_get_next_token;
8385 wakaba 1.123 next B;
8386 wakaba 1.102 } elsif ({
8387 wakaba 1.107 xmp => 1,
8388     iframe => 1,
8389     noembed => 1,
8390     noframes => 1,
8391     noscript => 0, ## TODO: 1 if scripting is enabled
8392 wakaba 1.102 }->{$token->{tag_name}}) {
8393 wakaba 1.107 if ($token->{tag_name} eq 'xmp') {
8394    
8395     $reconstruct_active_formatting_elements->($insert_to_current);
8396     } else {
8397    
8398 wakaba 1.1 }
8399 wakaba 1.107 ## NOTE: There is an "as if in body" code clone.
8400 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
8401 wakaba 1.123 next B;
8402 wakaba 1.54 } elsif ($token->{tag_name} eq 'isindex') {
8403 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'isindex', token => $token);
8404 wakaba 1.54
8405     if (defined $self->{form_element}) {
8406 wakaba 1.79
8407 wakaba 1.54 ## Ignore the token
8408 wakaba 1.122 ## NOTE: Not acknowledged.
8409 wakaba 1.54 $token = $self->_get_next_token;
8410 wakaba 1.123 next B;
8411 wakaba 1.54 } else {
8412     my $at = $token->{attributes};
8413     my $form_attrs;
8414     $form_attrs->{action} = $at->{action} if $at->{action};
8415     my $prompt_attr = $at->{prompt};
8416     $at->{name} = {name => 'name', value => 'isindex'};
8417     delete $at->{action};
8418     delete $at->{prompt};
8419     my @tokens = (
8420 wakaba 1.57 {type => START_TAG_TOKEN, tag_name => 'form',
8421 wakaba 1.112 attributes => $form_attrs,
8422     line => $token->{line}, column => $token->{column}},
8423     {type => START_TAG_TOKEN, tag_name => 'hr',
8424     line => $token->{line}, column => $token->{column}},
8425     {type => START_TAG_TOKEN, tag_name => 'p',
8426     line => $token->{line}, column => $token->{column}},
8427     {type => START_TAG_TOKEN, tag_name => 'label',
8428     line => $token->{line}, column => $token->{column}},
8429 wakaba 1.54 );
8430     if ($prompt_attr) {
8431 wakaba 1.79
8432 wakaba 1.112 push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
8433 wakaba 1.116 #line => $token->{line}, column => $token->{column},
8434     };
8435 wakaba 1.1 } else {
8436 wakaba 1.79
8437 wakaba 1.57 push @tokens, {type => CHARACTER_TOKEN,
8438 wakaba 1.112 data => 'This is a searchable index. Insert your search keywords here: ',
8439 wakaba 1.116 #line => $token->{line}, column => $token->{column},
8440     }; # SHOULD
8441 wakaba 1.54 ## TODO: make this configurable
8442 wakaba 1.1 }
8443 wakaba 1.54 push @tokens,
8444 wakaba 1.112 {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
8445     line => $token->{line}, column => $token->{column}},
8446 wakaba 1.57 #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
8447 wakaba 1.112 {type => END_TAG_TOKEN, tag_name => 'label',
8448     line => $token->{line}, column => $token->{column}},
8449     {type => END_TAG_TOKEN, tag_name => 'p',
8450     line => $token->{line}, column => $token->{column}},
8451     {type => START_TAG_TOKEN, tag_name => 'hr',
8452     line => $token->{line}, column => $token->{column}},
8453     {type => END_TAG_TOKEN, tag_name => 'form',
8454     line => $token->{line}, column => $token->{column}};
8455 wakaba 1.122 ## NOTE: Not acknowledged.
8456 wakaba 1.54 unshift @{$self->{token}}, (@tokens);
8457 wakaba 1.122 $token = $self->_get_next_token;
8458 wakaba 1.123 next B;
8459 wakaba 1.54 }
8460     } elsif ($token->{tag_name} eq 'textarea') {
8461     my $tag_name = $token->{tag_name};
8462     my $el;
8463    
8464     $el = $self->{document}->create_element_ns
8465 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8466 wakaba 1.54
8467     for my $attr_name (keys %{ $token->{attributes}}) {
8468 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8469 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8470 wakaba 1.117 $attr->value ($attr_t->{value});
8471     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8472     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8473     $el->set_attribute_node_ns ($attr);
8474 wakaba 1.54 }
8475    
8476 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8477     if defined $token->{line};
8478     $el->set_user_data (manakai_source_column => $token->{column})
8479     if defined $token->{column};
8480    
8481 wakaba 1.54
8482     ## TODO: $self->{form_element} if defined
8483     $self->{content_model} = RCDATA_CONTENT_MODEL;
8484     delete $self->{escape}; # MUST
8485    
8486     $insert->($el);
8487    
8488     my $text = '';
8489 wakaba 1.122
8490 wakaba 1.54 $token = $self->_get_next_token;
8491 wakaba 1.57 if ($token->{type} == CHARACTER_TOKEN) {
8492 wakaba 1.54 $token->{data} =~ s/^\x0A//;
8493 wakaba 1.53 unless (length $token->{data}) {
8494 wakaba 1.79
8495 wakaba 1.53 $token = $self->_get_next_token;
8496 wakaba 1.79 } else {
8497    
8498 wakaba 1.53 }
8499 wakaba 1.79 } else {
8500    
8501 wakaba 1.53 }
8502 wakaba 1.57 while ($token->{type} == CHARACTER_TOKEN) {
8503 wakaba 1.79
8504 wakaba 1.54 $text .= $token->{data};
8505     $token = $self->_get_next_token;
8506     }
8507     if (length $text) {
8508 wakaba 1.79
8509 wakaba 1.54 $el->manakai_append_text ($text);
8510     }
8511    
8512     $self->{content_model} = PCDATA_CONTENT_MODEL;
8513    
8514 wakaba 1.57 if ($token->{type} == END_TAG_TOKEN and
8515 wakaba 1.54 $token->{tag_name} eq $tag_name) {
8516 wakaba 1.79
8517 wakaba 1.54 ## Ignore the token
8518     } else {
8519 wakaba 1.79
8520 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in RCDATA:#'.$token->{type}, token => $token);
8521 wakaba 1.54 }
8522     $token = $self->_get_next_token;
8523 wakaba 1.123 next B;
8524     } elsif ($token->{tag_name} eq 'math' or
8525     $token->{tag_name} eq 'svg') {
8526     $reconstruct_active_formatting_elements->($insert_to_current);
8527 wakaba 1.128
8528     ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
8529    
8530     ## "adjust foreign attributes" - done in insert-element-f
8531 wakaba 1.123
8532    
8533     {
8534     my $el;
8535    
8536     $el = $self->{document}->create_element_ns
8537 wakaba 1.128 ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, [undef, $token->{tag_name}]);
8538 wakaba 1.123
8539     for my $attr_name (keys %{ $token->{attributes}}) {
8540     my $attr_t = $token->{attributes}->{$attr_name};
8541 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (
8542     @{
8543     $foreign_attr_xname->{$attr_name} ||
8544     [undef, [undef,
8545     $token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS eq $SVG_NS ?
8546     ($svg_attr_name->{$attr_name} || $attr_name) :
8547     $attr_name]]
8548     }
8549     );
8550 wakaba 1.123 $attr->value ($attr_t->{value});
8551     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8552     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8553     $el->set_attribute_node_ns ($attr);
8554     }
8555    
8556     $el->set_user_data (manakai_source_line => $token->{line})
8557     if defined $token->{line};
8558     $el->set_user_data (manakai_source_column => $token->{column})
8559     if defined $token->{column};
8560    
8561     $insert->($el);
8562     push @{$self->{open_elements}}, [$el, ($el_category_f->{$token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS}->{ $token->{tag_name}} || 0) | FOREIGN_EL];
8563 wakaba 1.128
8564     if ( $token->{attributes}->{xmlns} and $token->{attributes}->{xmlns}->{value} ne ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS)) {
8565     $self->{parse_error}->(level => $self->{must_level}, type => 'bad namespace', token => $token);
8566     ## TODO: Error type documentation
8567     }
8568 wakaba 1.123 }
8569    
8570    
8571     if ($self->{self_closing}) {
8572     pop @{$self->{open_elements}};
8573     delete $self->{self_closing};
8574     } else {
8575    
8576     $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
8577     ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
8578     ## mode, "in body" (not "in foreign content") secondary insertion
8579     ## mode, maybe.
8580     }
8581    
8582     $token = $self->_get_next_token;
8583     next B;
8584 wakaba 1.54 } elsif ({
8585     caption => 1, col => 1, colgroup => 1, frame => 1,
8586     frameset => 1, head => 1, option => 1, optgroup => 1,
8587     tbody => 1, td => 1, tfoot => 1, th => 1,
8588     thead => 1, tr => 1,
8589     }->{$token->{tag_name}}) {
8590 wakaba 1.79
8591 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'in body:'.$token->{tag_name}, token => $token);
8592 wakaba 1.54 ## Ignore the token
8593 wakaba 1.122 ## NOTE: |<col/>| or |<frame/>| here is an error.
8594 wakaba 1.54 $token = $self->_get_next_token;
8595 wakaba 1.123 next B;
8596 wakaba 1.54
8597     ## ISSUE: An issue on HTML5 new elements in the spec.
8598     } else {
8599 wakaba 1.108 if ($token->{tag_name} eq 'image') {
8600    
8601 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'image', token => $token);
8602 wakaba 1.108 $token->{tag_name} = 'img';
8603     } else {
8604    
8605     }
8606    
8607     ## NOTE: There is an "as if <br>" code clone.
8608 wakaba 1.54 $reconstruct_active_formatting_elements->($insert_to_current);
8609 wakaba 1.53
8610 wakaba 1.54
8611     {
8612     my $el;
8613    
8614     $el = $self->{document}->create_element_ns
8615 wakaba 1.128 ($HTML_NS, [undef, $token->{tag_name}]);
8616 wakaba 1.54
8617     for my $attr_name (keys %{ $token->{attributes}}) {
8618 wakaba 1.117 my $attr_t = $token->{attributes}->{$attr_name};
8619 wakaba 1.128 my $attr = $self->{document}->create_attribute_ns (undef, [undef, $attr_name]);
8620 wakaba 1.117 $attr->value ($attr_t->{value});
8621     $attr->set_user_data (manakai_source_line => $attr_t->{line});
8622     $attr->set_user_data (manakai_source_column => $attr_t->{column});
8623     $el->set_attribute_node_ns ($attr);
8624 wakaba 1.53 }
8625 wakaba 1.54
8626 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8627     if defined $token->{line};
8628     $el->set_user_data (manakai_source_column => $token->{column})
8629     if defined $token->{column};
8630    
8631 wakaba 1.54 $insert->($el);
8632 wakaba 1.121 push @{$self->{open_elements}}, [$el, $el_category->{$token->{tag_name}} || 0];
8633 wakaba 1.54 }
8634    
8635 wakaba 1.107
8636 wakaba 1.108 if ({
8637     applet => 1, marquee => 1, object => 1,
8638     }->{$token->{tag_name}}) {
8639    
8640     push @$active_formatting_elements, ['#marker', ''];
8641 wakaba 1.122
8642 wakaba 1.108 } elsif ({
8643     b => 1, big => 1, em => 1, font => 1, i => 1,
8644     s => 1, small => 1, strile => 1,
8645     strong => 1, tt => 1, u => 1,
8646     }->{$token->{tag_name}}) {
8647    
8648     push @$active_formatting_elements, $self->{open_elements}->[-1];
8649 wakaba 1.122
8650 wakaba 1.108 } elsif ($token->{tag_name} eq 'input') {
8651    
8652     ## TODO: associate with $self->{form_element} if defined
8653     pop @{$self->{open_elements}};
8654 wakaba 1.122 delete $self->{self_closing};
8655 wakaba 1.108 } elsif ({
8656     area => 1, basefont => 1, bgsound => 1, br => 1,
8657     embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
8658     #image => 1,
8659     }->{$token->{tag_name}}) {
8660    
8661     pop @{$self->{open_elements}};
8662 wakaba 1.122 delete $self->{self_closing};
8663 wakaba 1.108 } elsif ($token->{tag_name} eq 'select') {
8664 wakaba 1.107 ## TODO: associate with $self->{form_element} if defined
8665    
8666     if ($self->{insertion_mode} & TABLE_IMS or
8667     $self->{insertion_mode} & BODY_TABLE_IMS or
8668     $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
8669    
8670     $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
8671     } else {
8672    
8673     $self->{insertion_mode} = IN_SELECT_IM;
8674     }
8675 wakaba 1.122
8676 wakaba 1.108 } else {
8677    
8678 wakaba 1.107 }
8679 wakaba 1.53
8680 wakaba 1.54 $token = $self->_get_next_token;
8681 wakaba 1.123 next B;
8682 wakaba 1.54 }
8683 wakaba 1.57 } elsif ($token->{type} == END_TAG_TOKEN) {
8684 wakaba 1.54 if ($token->{tag_name} eq 'body') {
8685 wakaba 1.105 ## has a |body| element in scope
8686     my $i;
8687 wakaba 1.109 INSCOPE: {
8688     for (reverse @{$self->{open_elements}}) {
8689 wakaba 1.121 if ($_->[1] & BODY_EL) {
8690 wakaba 1.109
8691     $i = $_;
8692     last INSCOPE;
8693 wakaba 1.121 } elsif ($_->[1] & SCOPING_EL) {
8694 wakaba 1.109
8695     last;
8696     }
8697 wakaba 1.54 }
8698 wakaba 1.109
8699     $self->{parse_error}->(level => $self->{must_level}, type => 'start tag not allowed',
8700 wakaba 1.111 value => $token->{tag_name}, token => $token);
8701 wakaba 1.105 ## NOTE: Ignore the token.
8702 wakaba 1.54 $token = $self->_get_next_token;
8703 wakaba 1.123 next B;
8704 wakaba 1.109 } # INSCOPE
8705 wakaba 1.105
8706     for (@{$self->{open_elements}}) {
8707 wakaba 1.121 unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
8708 wakaba 1.105
8709 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
8710     value => $_->[0]->manakai_local_name,
8711     token => $token);
8712 wakaba 1.105 last;
8713     } else {
8714    
8715     }
8716     }
8717    
8718     $self->{insertion_mode} = AFTER_BODY_IM;
8719     $token = $self->_get_next_token;
8720 wakaba 1.123 next B;
8721 wakaba 1.54 } elsif ($token->{tag_name} eq 'html') {
8722 wakaba 1.120 ## TODO: Update this code. It seems that the code below is not
8723     ## up-to-date, though it has same effect as speced.
8724 wakaba 1.121 if (@{$self->{open_elements}} > 1 and
8725     $self->{open_elements}->[1]->[1] & BODY_EL) {
8726 wakaba 1.54 ## ISSUE: There is an issue in the spec.
8727 wakaba 1.121 unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
8728 wakaba 1.79
8729 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
8730     value => $self->{open_elements}->[1]->[0]
8731     ->manakai_local_name,
8732     token => $token);
8733 wakaba 1.79 } else {
8734    
8735 wakaba 1.54 }
8736 wakaba 1.56 $self->{insertion_mode} = AFTER_BODY_IM;
8737 wakaba 1.54 ## reprocess
8738 wakaba 1.123 next B;
8739 wakaba 1.54 } else {
8740 wakaba 1.79
8741 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
8742 wakaba 1.54 ## Ignore the token
8743     $token = $self->_get_next_token;
8744 wakaba 1.123 next B;
8745 wakaba 1.53 }
8746 wakaba 1.54 } elsif ({
8747     address => 1, blockquote => 1, center => 1, dir => 1,
8748     div => 1, dl => 1, fieldset => 1, listing => 1,
8749     menu => 1, ol => 1, pre => 1, ul => 1,
8750     dd => 1, dt => 1, li => 1,
8751 wakaba 1.102 applet => 1, button => 1, marquee => 1, object => 1,
8752 wakaba 1.54 }->{$token->{tag_name}}) {
8753     ## has an element in scope
8754     my $i;
8755     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8756     my $node = $self->{open_elements}->[$_];
8757 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
8758 wakaba 1.79
8759 wakaba 1.54 $i = $_;
8760 wakaba 1.87 last INSCOPE;
8761 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
8762 wakaba 1.79
8763 wakaba 1.54 last INSCOPE;
8764     }
8765     } # INSCOPE
8766 wakaba 1.89
8767     unless (defined $i) { # has an element in scope
8768    
8769 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
8770 wakaba 1.89 } else {
8771     ## Step 1. generate implied end tags
8772     while ({
8773     dd => ($token->{tag_name} ne 'dd'),
8774     dt => ($token->{tag_name} ne 'dt'),
8775     li => ($token->{tag_name} ne 'li'),
8776     p => 1,
8777 wakaba 1.121 }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
8778 wakaba 1.89
8779     pop @{$self->{open_elements}};
8780     }
8781    
8782     ## Step 2.
8783 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
8784     ne $token->{tag_name}) {
8785 wakaba 1.79
8786 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
8787     value => $self->{open_elements}->[-1]->[0]
8788     ->manakai_local_name,
8789     token => $token);
8790 wakaba 1.1 } else {
8791 wakaba 1.79
8792 wakaba 1.1 }
8793 wakaba 1.89
8794     ## Step 3.
8795 wakaba 1.54 splice @{$self->{open_elements}}, $i;
8796 wakaba 1.89
8797     ## Step 4.
8798     $clear_up_to_marker->()
8799     if {
8800 wakaba 1.102 applet => 1, button => 1, marquee => 1, object => 1,
8801 wakaba 1.89 }->{$token->{tag_name}};
8802 wakaba 1.54 }
8803     $token = $self->_get_next_token;
8804 wakaba 1.123 next B;
8805 wakaba 1.54 } elsif ($token->{tag_name} eq 'form') {
8806 wakaba 1.92 undef $self->{form_element};
8807    
8808 wakaba 1.54 ## has an element in scope
8809 wakaba 1.92 my $i;
8810 wakaba 1.54 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8811     my $node = $self->{open_elements}->[$_];
8812 wakaba 1.121 if ($node->[1] & FORM_EL) {
8813 wakaba 1.79
8814 wakaba 1.92 $i = $_;
8815 wakaba 1.54 last INSCOPE;
8816 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
8817 wakaba 1.79
8818 wakaba 1.54 last INSCOPE;
8819 wakaba 1.36 }
8820 wakaba 1.54 } # INSCOPE
8821 wakaba 1.92
8822     unless (defined $i) { # has an element in scope
8823 wakaba 1.79
8824 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
8825 wakaba 1.54 } else {
8826 wakaba 1.92 ## Step 1. generate implied end tags
8827 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
8828 wakaba 1.92
8829     pop @{$self->{open_elements}};
8830     }
8831    
8832     ## Step 2.
8833 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
8834     ne $token->{tag_name}) {
8835 wakaba 1.92
8836 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
8837     value => $self->{open_elements}->[-1]->[0]
8838     ->manakai_local_name,
8839     token => $token);
8840 wakaba 1.92 } else {
8841    
8842     }
8843 wakaba 1.79
8844 wakaba 1.92 ## Step 3.
8845     splice @{$self->{open_elements}}, $i;
8846 wakaba 1.36 }
8847    
8848 wakaba 1.54 $token = $self->_get_next_token;
8849 wakaba 1.123 next B;
8850 wakaba 1.54 } elsif ({
8851     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
8852     }->{$token->{tag_name}}) {
8853     ## has an element in scope
8854     my $i;
8855     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8856     my $node = $self->{open_elements}->[$_];
8857 wakaba 1.121 if ($node->[1] & HEADING_EL) {
8858 wakaba 1.79
8859 wakaba 1.54 $i = $_;
8860     last INSCOPE;
8861 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
8862 wakaba 1.79
8863 wakaba 1.54 last INSCOPE;
8864 wakaba 1.53 }
8865 wakaba 1.54 } # INSCOPE
8866 wakaba 1.93
8867     unless (defined $i) { # has an element in scope
8868 wakaba 1.79
8869 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
8870 wakaba 1.79 } else {
8871 wakaba 1.93 ## Step 1. generate implied end tags
8872 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
8873 wakaba 1.93
8874     pop @{$self->{open_elements}};
8875     }
8876 wakaba 1.79
8877 wakaba 1.93 ## Step 2.
8878 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
8879     ne $token->{tag_name}) {
8880 wakaba 1.93
8881 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
8882 wakaba 1.93 } else {
8883    
8884     }
8885    
8886     ## Step 3.
8887     splice @{$self->{open_elements}}, $i;
8888 wakaba 1.53 }
8889    
8890 wakaba 1.54 $token = $self->_get_next_token;
8891 wakaba 1.123 next B;
8892 wakaba 1.87 } elsif ($token->{tag_name} eq 'p') {
8893     ## has an element in scope
8894     my $i;
8895     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
8896     my $node = $self->{open_elements}->[$_];
8897 wakaba 1.121 if ($node->[1] & P_EL) {
8898 wakaba 1.87
8899     $i = $_;
8900 wakaba 1.88 last INSCOPE;
8901 wakaba 1.121 } elsif ($node->[1] & SCOPING_EL) {
8902 wakaba 1.87
8903     last INSCOPE;
8904     }
8905     } # INSCOPE
8906 wakaba 1.91
8907     if (defined $i) {
8908 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
8909     ne $token->{tag_name}) {
8910 wakaba 1.87
8911 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
8912     value => $self->{open_elements}->[-1]->[0]
8913     ->manakai_local_name,
8914     token => $token);
8915 wakaba 1.87 } else {
8916    
8917     }
8918 wakaba 1.91
8919 wakaba 1.87 splice @{$self->{open_elements}}, $i;
8920     } else {
8921    
8922 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
8923 wakaba 1.91
8924    
8925 wakaba 1.87 ## As if <p>, then reprocess the current token
8926     my $el;
8927    
8928     $el = $self->{document}->create_element_ns
8929 wakaba 1.128 ($HTML_NS, [undef, 'p']);
8930 wakaba 1.87
8931 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8932     if defined $token->{line};
8933     $el->set_user_data (manakai_source_column => $token->{column})
8934     if defined $token->{column};
8935    
8936 wakaba 1.87 $insert->($el);
8937 wakaba 1.91 ## NOTE: Not inserted into |$self->{open_elements}|.
8938 wakaba 1.87 }
8939 wakaba 1.91
8940 wakaba 1.87 $token = $self->_get_next_token;
8941 wakaba 1.123 next B;
8942 wakaba 1.54 } elsif ({
8943     a => 1,
8944     b => 1, big => 1, em => 1, font => 1, i => 1,
8945     nobr => 1, s => 1, small => 1, strile => 1,
8946     strong => 1, tt => 1, u => 1,
8947     }->{$token->{tag_name}}) {
8948 wakaba 1.79
8949 wakaba 1.111 $formatting_end_tag->($token);
8950 wakaba 1.123 next B;
8951 wakaba 1.54 } elsif ($token->{tag_name} eq 'br') {
8952 wakaba 1.79
8953 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:br', token => $token);
8954 wakaba 1.53
8955 wakaba 1.54 ## As if <br>
8956     $reconstruct_active_formatting_elements->($insert_to_current);
8957    
8958     my $el;
8959    
8960 wakaba 1.1 $el = $self->{document}->create_element_ns
8961 wakaba 1.128 ($HTML_NS, [undef, 'br']);
8962 wakaba 1.1
8963 wakaba 1.114 $el->set_user_data (manakai_source_line => $token->{line})
8964     if defined $token->{line};
8965     $el->set_user_data (manakai_source_column => $token->{column})
8966     if defined $token->{column};
8967    
8968 wakaba 1.54 $insert->($el);
8969    
8970     ## Ignore the token.
8971     $token = $self->_get_next_token;
8972 wakaba 1.123 next B;
8973 wakaba 1.54 } elsif ({
8974     caption => 1, col => 1, colgroup => 1, frame => 1,
8975     frameset => 1, head => 1, option => 1, optgroup => 1,
8976     tbody => 1, td => 1, tfoot => 1, th => 1,
8977     thead => 1, tr => 1,
8978     area => 1, basefont => 1, bgsound => 1,
8979     embed => 1, hr => 1, iframe => 1, image => 1,
8980     img => 1, input => 1, isindex => 1, noembed => 1,
8981     noframes => 1, param => 1, select => 1, spacer => 1,
8982     table => 1, textarea => 1, wbr => 1,
8983     noscript => 0, ## TODO: if scripting is enabled
8984     }->{$token->{tag_name}}) {
8985 wakaba 1.79
8986 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
8987 wakaba 1.54 ## Ignore the token
8988     $token = $self->_get_next_token;
8989 wakaba 1.123 next B;
8990 wakaba 1.54
8991     ## ISSUE: Issue on HTML5 new elements in spec
8992    
8993     } else {
8994     ## Step 1
8995     my $node_i = -1;
8996     my $node = $self->{open_elements}->[$node_i];
8997    
8998     ## Step 2
8999     S2: {
9000 wakaba 1.121 if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
9001 wakaba 1.54 ## Step 1
9002     ## generate implied end tags
9003 wakaba 1.121 while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
9004 wakaba 1.79
9005 wakaba 1.83 ## ISSUE: Can this case be reached?
9006 wakaba 1.86 pop @{$self->{open_elements}};
9007 wakaba 1.54 }
9008    
9009     ## Step 2
9010 wakaba 1.121 if ($self->{open_elements}->[-1]->[0]->manakai_local_name
9011     ne $token->{tag_name}) {
9012 wakaba 1.79
9013 wakaba 1.60 ## NOTE: <x><y></x>
9014 wakaba 1.120 $self->{parse_error}->(level => $self->{must_level}, type => 'not closed',
9015     value => $self->{open_elements}->[-1]->[0]
9016     ->manakai_local_name,
9017     token => $token);
9018 wakaba 1.79 } else {
9019    
9020 wakaba 1.54 }
9021    
9022     ## Step 3
9023     splice @{$self->{open_elements}}, $node_i;
9024 wakaba 1.53
9025 wakaba 1.36 $token = $self->_get_next_token;
9026 wakaba 1.54 last S2;
9027 wakaba 1.1 } else {
9028 wakaba 1.54 ## Step 3
9029 wakaba 1.121 if (not ($node->[1] & FORMATTING_EL) and
9030 wakaba 1.54 #not $phrasing_category->{$node->[1]} and
9031 wakaba 1.121 ($node->[1] & SPECIAL_EL or
9032     $node->[1] & SCOPING_EL)) {
9033 wakaba 1.79
9034 wakaba 1.111 $self->{parse_error}->(level => $self->{must_level}, type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
9035 wakaba 1.54 ## Ignore the token
9036     $token = $self->_get_next_token;
9037     last S2;
9038     }
9039 wakaba 1.79
9040    
9041 wakaba 1.1 }
9042 wakaba 1.54
9043     ## Step 4
9044     $node_i--;
9045     $node = $self->{open_elements}->[$node_i];
9046    
9047     ## Step 5;
9048     redo S2;
9049     } # S2
9050 wakaba 1.123 next B;
9051 wakaba 1.1 }
9052     }
9053 wakaba 1.123 next B;
9054     } continue { # B
9055     if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
9056     ## NOTE: The code below is executed in cases where it does not have
9057     ## to be, but it it is harmless even in those cases.
9058     ## has an element in scope
9059     INSCOPE: {
9060     for (reverse 0..$#{$self->{open_elements}}) {
9061     my $node = $self->{open_elements}->[$_];
9062     if ($node->[1] & FOREIGN_EL) {
9063     last INSCOPE;
9064     } elsif ($node->[1] & SCOPING_EL) {
9065     last;
9066     }
9067     }
9068    
9069     ## NOTE: No foreign element in scope.
9070     $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
9071     } # INSCOPE
9072     }
9073 wakaba 1.1 } # B
9074    
9075     ## Stop parsing # MUST
9076    
9077     ## TODO: script stuffs
9078 wakaba 1.3 } # _tree_construct_main
9079    
9080     sub set_inner_html ($$$) {
9081     my $class = shift;
9082     my $node = shift;
9083     my $s = \$_[0];
9084     my $onerror = $_[1];
9085    
9086 wakaba 1.65 ## ISSUE: Should {confident} be true?
9087    
9088 wakaba 1.3 my $nt = $node->node_type;
9089     if ($nt == 9) {
9090     # MUST
9091    
9092     ## Step 1 # MUST
9093     ## TODO: If the document has an active parser, ...
9094     ## ISSUE: There is an issue in the spec.
9095    
9096     ## Step 2 # MUST
9097     my @cn = @{$node->child_nodes};
9098     for (@cn) {
9099     $node->remove_child ($_);
9100     }
9101    
9102     ## Step 3, 4, 5 # MUST
9103     $class->parse_string ($$s => $node, $onerror);
9104     } elsif ($nt == 1) {
9105     ## TODO: If non-html element
9106    
9107     ## NOTE: Most of this code is copied from |parse_string|
9108    
9109     ## Step 1 # MUST
9110 wakaba 1.14 my $this_doc = $node->owner_document;
9111     my $doc = $this_doc->implementation->create_document;
9112 wakaba 1.18 $doc->manakai_is_html (1);
9113 wakaba 1.3 my $p = $class->new;
9114     $p->{document} = $doc;
9115    
9116 wakaba 1.84 ## Step 8 # MUST
9117 wakaba 1.3 my $i = 0;
9118 wakaba 1.119 $p->{line_prev} = $p->{line} = 1;
9119     $p->{column_prev} = $p->{column} = 0;
9120 wakaba 1.76 $p->{set_next_char} = sub {
9121 wakaba 1.3 my $self = shift;
9122 wakaba 1.14
9123 wakaba 1.76 pop @{$self->{prev_char}};
9124     unshift @{$self->{prev_char}}, $self->{next_char};
9125 wakaba 1.14
9126 wakaba 1.76 $self->{next_char} = -1 and return if $i >= length $$s;
9127     $self->{next_char} = ord substr $$s, $i++, 1;
9128 wakaba 1.119
9129     ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
9130     $p->{column}++;
9131 wakaba 1.4
9132 wakaba 1.76 if ($self->{next_char} == 0x000A) { # LF
9133 wakaba 1.119 $p->{line}++;
9134     $p->{column} = 0;
9135 wakaba 1.79
9136 wakaba 1.76 } elsif ($self->{next_char} == 0x000D) { # CR
9137 wakaba 1.15 $i++ if substr ($$s, $i, 1) eq "\x0A";
9138 wakaba 1.76 $self->{next_char} = 0x000A; # LF # MUST
9139 wakaba 1.119 $p->{line}++;
9140     $p->{column} = 0;
9141 wakaba 1.79
9142 wakaba 1.76 } elsif ($self->{next_char} > 0x10FFFF) {
9143     $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
9144 wakaba 1.79
9145 wakaba 1.76 } elsif ($self->{next_char} == 0x0000) { # NULL
9146 wakaba 1.79
9147 wakaba 1.106 $self->{parse_error}->(level => $self->{must_level}, type => 'NULL');
9148 wakaba 1.76 $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
9149 wakaba 1.129 } elsif ($self->{next_char} <= 0x0008 or
9150     (0x000E <= $self->{next_char} and
9151     $self->{next_char} <= 0x001F) or
9152     (0x007F <= $self->{next_char} and
9153     $self->{next_char} <= 0x009F) or
9154     (0xD800 <= $self->{next_char} and
9155     $self->{next_char} <= 0xDFFF) or
9156     (0xFDD0 <= $self->{next_char} and
9157     $self->{next_char} <= 0xFDDF) or
9158     {
9159     0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,
9160     0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,
9161     0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,
9162     0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,
9163     0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,
9164     0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,
9165     0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,
9166     0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,
9167     0x10FFFE => 1, 0x10FFFF => 1,
9168     }->{$self->{next_char}}) {
9169    
9170     $self->{parse_error}->(level => $self->{must_level}, type => 'control char', level => $self->{must_level});
9171     ## TODO: error type documentation
9172 wakaba 1.3 }
9173     };
9174 wakaba 1.76 $p->{prev_char} = [-1, -1, -1];
9175     $p->{next_char} = -1;
9176 wakaba 1.3
9177     my $ponerror = $onerror || sub {
9178     my (%opt) = @_;
9179 wakaba 1.119 my $line = $opt{line};
9180     my $column = $opt{column};
9181     if (defined $opt{token} and defined $opt{token}->{line}) {
9182     $line = $opt{token}->{line};
9183     $column = $opt{token}->{column};
9184     }
9185     warn "Parse error ($opt{type}) at line $line column $column\n";
9186 wakaba 1.3 };
9187     $p->{parse_error} = sub {
9188 wakaba 1.119 $ponerror->(line => $p->{line}, column => $p->{column}, @_);
9189 wakaba 1.3 };
9190    
9191     $p->_initialize_tokenizer;
9192     $p->_initialize_tree_constructor;
9193    
9194     ## Step 2
9195 wakaba 1.71 my $node_ln = $node->manakai_local_name;
9196 wakaba 1.41 $p->{content_model} = {
9197     title => RCDATA_CONTENT_MODEL,
9198     textarea => RCDATA_CONTENT_MODEL,
9199     style => CDATA_CONTENT_MODEL,
9200     script => CDATA_CONTENT_MODEL,
9201     xmp => CDATA_CONTENT_MODEL,
9202     iframe => CDATA_CONTENT_MODEL,
9203     noembed => CDATA_CONTENT_MODEL,
9204     noframes => CDATA_CONTENT_MODEL,
9205     noscript => CDATA_CONTENT_MODEL,
9206     plaintext => PLAINTEXT_CONTENT_MODEL,
9207     }->{$node_ln};
9208     $p->{content_model} = PCDATA_CONTENT_MODEL
9209     unless defined $p->{content_model};
9210     ## ISSUE: What is "the name of the element"? local name?
9211 wakaba 1.3
9212 wakaba 1.121 $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
9213     ## TODO: Foreign element OK?
9214 wakaba 1.3
9215 wakaba 1.84 ## Step 3
9216 wakaba 1.3 my $root = $doc->create_element_ns
9217     ('http://www.w3.org/1999/xhtml', [undef, 'html']);
9218    
9219 wakaba 1.84 ## Step 4 # MUST
9220 wakaba 1.3 $doc->append_child ($root);
9221    
9222 wakaba 1.84 ## Step 5 # MUST
9223 wakaba 1.121 push @{$p->{open_elements}}, [$root, $el_category->{html}];
9224 wakaba 1.3
9225     undef $p->{head_element};
9226    
9227 wakaba 1.84 ## Step 6 # MUST
9228 wakaba 1.3 $p->_reset_insertion_mode;
9229    
9230 wakaba 1.84 ## Step 7 # MUST
9231 wakaba 1.3 my $anode = $node;
9232     AN: while (defined $anode) {
9233     if ($anode->node_type == 1) {
9234     my $nsuri = $anode->namespace_uri;
9235     if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
9236 wakaba 1.71 if ($anode->manakai_local_name eq 'form') {
9237 wakaba 1.79
9238 wakaba 1.3 $p->{form_element} = $anode;
9239     last AN;
9240     }
9241     }
9242     }
9243     $anode = $anode->parent_node;
9244     } # AN
9245    
9246 wakaba 1.84 ## Step 9 # MUST
9247 wakaba 1.3 {
9248     my $self = $p;
9249     $token = $self->_get_next_token;
9250     }
9251     $p->_tree_construction_main;
9252    
9253 wakaba 1.84 ## Step 10 # MUST
9254 wakaba 1.3 my @cn = @{$node->child_nodes};
9255     for (@cn) {
9256     $node->remove_child ($_);
9257     }
9258     ## ISSUE: mutation events? read-only?
9259    
9260 wakaba 1.84 ## Step 11 # MUST
9261 wakaba 1.3 @cn = @{$root->child_nodes};
9262     for (@cn) {
9263 wakaba 1.14 $this_doc->adopt_node ($_);
9264 wakaba 1.3 $node->append_child ($_);
9265     }
9266 wakaba 1.14 ## ISSUE: mutation events?
9267 wakaba 1.3
9268     $p->_terminate_tree_constructor;
9269 wakaba 1.119
9270     delete $p->{parse_error}; # delete loop
9271 wakaba 1.3 } else {
9272     die "$0: |set_inner_html| is not defined for node of type $nt";
9273     }
9274     } # set_inner_html
9275    
9276     } # tree construction stage
9277 wakaba 1.1
9278 wakaba 1.65 package Whatpm::HTML::RestartParser;
9279     push our @ISA, 'Error';
9280    
9281 wakaba 1.1 1;
9282 wakaba 1.137 # $Date: 2008/05/24 04:26:27 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24