/[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.132 - (hide annotations) (download)
Sat May 17 07:31:49 2008 UTC (18 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.131: +18 -9 lines
++ whatpm/Whatpm/ChangeLog	17 May 2008 07:31:24 -0000
	* HTML.pm.src (parse_char_stream): New method.
	(parse_char_string): This method is now defined as an invocation
	of the |parse_char_stream| method.

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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24