/[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.125 - (hide annotations) (download)
Sat Apr 12 15:31:55 2008 UTC (18 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.124: +3 -3 lines
++ whatpm/Whatpm/ChangeLog	12 Apr 2008 15:31:52 -0000
	* HTML.pm.src: Support for new long MathML entities (HTML5
	revision 1406).

2008-04-13  Wakaba  <wakaba@suika.fam.cx>

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24