/[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.142 - (hide annotations) (download)
Sat May 24 11:57:47 2008 UTC (18 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.141: +4 -7 lines
++ whatpm/t/ChangeLog	24 May 2008 11:50:28 -0000
	* content-model-2.dat: Test data on sandbox="" and seemless=""
	are added (cf. HTML5 revision 1637 and 1643).

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

++ whatpm/Whatpm/ChangeLog	24 May 2008 11:16:23 -0000
	* HTML.pm.src: ";" is not part of charset name (HTML5 revision 1665).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	24 May 2008 11:50:15 -0000
2008-05-24  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Support for seemless="" (HTML5 revision 1637) and
	sandbox="" (HTML5 revision 1643).

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

	* HTML.pm: |width| and |height| on |iframe| are now conforming (HTML5
	revision 1659).

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24