/[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.115 - (hide annotations) (download)
Wed Mar 19 23:43:47 2008 UTC (18 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.114: +2 -32 lines
++ whatpm/Whatpm/ChangeLog	19 Mar 2008 23:42:08 -0000
2008-03-20  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm.src (_get_next_token): Remove |first_start_tag|
	flag, which is no longer used.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24