/[suikacvs]/markup/html/whatpm/Whatpm/HTML.pm.src
Suika

Contents of /markup/html/whatpm/Whatpm/HTML.pm.src

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.122 - (hide annotations) (download) (as text)
Sun Apr 6 06:34:11 2008 UTC (18 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.121: +63 -19 lines
File MIME type: application/x-wais-source
++ whatpm/Whatpm/ChangeLog	6 Apr 2008 06:29:50 -0000
2008-04-06  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm.src: Don't use local name stored in stack (i.e. ->[1])
	for error reporting.  (This is a preparation for using constant
	value for ->[1].)

1 wakaba 1.2 package Whatpm::HTML;
2 wakaba 1.1 use strict;
3 wakaba 1.122 our $VERSION=do{my @r=(q$Revision: 1.121 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 wakaba 1.63 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.70 ## 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    
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.10 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.103 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.63 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.64 require Encode; ## TODO: decode(utf8) don't delete BOM
94 wakaba 1.63 $s = \ (Encode::decode ($charset, $$bytes_s));
95 wakaba 1.64 $self->{input_encoding} = lc $charset; ## TODO: normalize name
96 wakaba 1.63 $self->{confident} = 1;
97     } else {
98 wakaba 1.65 ## 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.64 $s = \ (Encode::decode ($charset, $$bytes_s));
104     $self->{input_encoding} = $charset;
105 wakaba 1.63 $self->{confident} = 0;
106     }
107    
108     $self->{change_encoding} = sub {
109     my $self = shift;
110     my $charset = lc shift;
111 wakaba 1.114 my $token = shift;
112 wakaba 1.63 ## 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.64 !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.
130 wakaba 1.114 ':'.$charset, level => 'w', token => $token);
131 wakaba 1.63
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.64 $self->{input_encoding} = $charset; ## TODO: normalize
151 wakaba 1.63 $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.63 *parse_char_string = \&parse_string;
167    
168 wakaba 1.1 sub parse_string ($$$;$) {
169 wakaba 1.63 my $self = ref $_[0] ? shift : shift->new;
170     my $s = ref $_[0] ? $_[0] : \($_[0]);
171 wakaba 1.1 $self->{document} = $_[1];
172 wakaba 1.63 @{$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.63 $self->{confident} = 1 unless exists $self->{confident};
177 wakaba 1.64 $self->{document}->input_encoding ($self->{input_encoding})
178     if defined $self->{input_encoding};
179 wakaba 1.63
180 wakaba 1.1 my $i = 0;
181 wakaba 1.112 $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.112
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.112 $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.112 $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.8 !!!parse-error (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.112 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.112 $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.112 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.63 $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.61 $self->{application_cache_selection} = sub {
250     #
251     };
252 wakaba 1.1 return $self;
253     } # new
254    
255 wakaba 1.40 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.57 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.57
299 wakaba 1.55 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.54 sub AFTER_HTML_IMS () { 0b100 }
307     sub HEAD_IMS () { 0b1000 }
308     sub BODY_IMS () { 0b10000 }
309 wakaba 1.56 sub BODY_TABLE_IMS () { 0b100000 }
310 wakaba 1.54 sub TABLE_IMS () { 0b1000000 }
311 wakaba 1.56 sub ROW_IMS () { 0b10000000 }
312 wakaba 1.54 sub BODY_AFTER_IMS () { 0b100000000 }
313     sub FRAME_IMS () { 0b1000000000 }
314 wakaba 1.101 sub SELECT_IMS () { 0b10000000000 }
315 wakaba 1.54
316 wakaba 1.84 ## NOTE: "initial" and "before html" insertion modes have no constants.
317    
318     ## NOTE: "after after body" insertion mode.
319 wakaba 1.54 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.54 sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
323 wakaba 1.84
324 wakaba 1.54 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.56 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.54 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.54 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.57 $self->{state} = DATA_STATE; # MUST
346 wakaba 1.40 $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 !!!next-input-character;
354     $self->{token} = [];
355 wakaba 1.18 # $self->{escape}
356 wakaba 1.1 } # _initialize_tokenizer
357    
358     ## A token has:
359 wakaba 1.55 ## ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
360     ## CHARACTER_TOKEN, or END_OF_FILE_TOKEN
361     ## ->{name} (DOCTYPE_TOKEN)
362     ## ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
363     ## ->{public_identifier} (DOCTYPE_TOKEN)
364     ## ->{system_identifier} (DOCTYPE_TOKEN)
365 wakaba 1.75 ## ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
366 wakaba 1.55 ## ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
367 wakaba 1.66 ## ->{name}
368     ## ->{value}
369     ## ->{has_reference} == 1 or 0
370 wakaba 1.55 ## ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
371 wakaba 1.1
372     ## Emitted token MUST immediately be handled by the tree construction state.
373    
374     ## Before each step, UA MAY check to see if either one of the scripts in
375     ## "list of scripts that will execute as soon as possible" or the first
376     ## script in the "list of scripts that will execute asynchronously",
377     ## has completed loading. If one has, then it MUST be executed
378     ## and removed from the list.
379    
380 wakaba 1.59 ## NOTE: HTML5 "Writing HTML documents" section, applied to
381     ## documents and not to user agents and conformance checkers,
382     ## contains some requirements that are not detected by the
383     ## parsing algorithm:
384     ## - Some requirements on character encoding declarations. ## TODO
385     ## - "Elements MUST NOT contain content that their content model disallows."
386     ## ... Some are parse error, some are not (will be reported by c.c.).
387     ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO
388     ## - Text (in elements, attributes, and comments) SHOULD NOT contain
389     ## control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL? Unicode control character?)
390    
391     ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot
392     ## be detected by the HTML5 parsing algorithm:
393     ## - Text,
394    
395 wakaba 1.1 sub _get_next_token ($) {
396     my $self = shift;
397     if (@{$self->{token}}) {
398     return shift @{$self->{token}};
399     }
400    
401     A: {
402 wakaba 1.57 if ($self->{state} == DATA_STATE) {
403 wakaba 1.76 if ($self->{next_char} == 0x0026) { # &
404 wakaba 1.72 if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
405     not $self->{escape}) {
406 wakaba 1.77 !!!cp (1);
407 wakaba 1.57 $self->{state} = ENTITY_DATA_STATE;
408 wakaba 1.1 !!!next-input-character;
409     redo A;
410     } else {
411 wakaba 1.77 !!!cp (2);
412 wakaba 1.1 #
413     }
414 wakaba 1.76 } elsif ($self->{next_char} == 0x002D) { # -
415 wakaba 1.40 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
416 wakaba 1.13 unless ($self->{escape}) {
417 wakaba 1.76 if ($self->{prev_char}->[0] == 0x002D and # -
418     $self->{prev_char}->[1] == 0x0021 and # !
419     $self->{prev_char}->[2] == 0x003C) { # <
420 wakaba 1.77 !!!cp (3);
421 wakaba 1.13 $self->{escape} = 1;
422 wakaba 1.77 } else {
423     !!!cp (4);
424 wakaba 1.13 }
425 wakaba 1.77 } else {
426     !!!cp (5);
427 wakaba 1.13 }
428     }
429    
430     #
431 wakaba 1.76 } elsif ($self->{next_char} == 0x003C) { # <
432 wakaba 1.40 if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
433     (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
434 wakaba 1.13 not $self->{escape})) {
435 wakaba 1.77 !!!cp (6);
436 wakaba 1.57 $self->{state} = TAG_OPEN_STATE;
437 wakaba 1.1 !!!next-input-character;
438     redo A;
439     } else {
440 wakaba 1.77 !!!cp (7);
441 wakaba 1.1 #
442     }
443 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
444 wakaba 1.13 if ($self->{escape} and
445 wakaba 1.40 ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
446 wakaba 1.76 if ($self->{prev_char}->[0] == 0x002D and # -
447     $self->{prev_char}->[1] == 0x002D) { # -
448 wakaba 1.77 !!!cp (8);
449 wakaba 1.13 delete $self->{escape};
450 wakaba 1.77 } else {
451     !!!cp (9);
452 wakaba 1.13 }
453 wakaba 1.77 } else {
454     !!!cp (10);
455 wakaba 1.13 }
456    
457     #
458 wakaba 1.76 } elsif ($self->{next_char} == -1) {
459 wakaba 1.77 !!!cp (11);
460 wakaba 1.112 !!!emit ({type => END_OF_FILE_TOKEN,
461     line => $self->{line}, column => $self->{column}});
462 wakaba 1.1 last A; ## TODO: ok?
463 wakaba 1.77 } else {
464     !!!cp (12);
465 wakaba 1.1 }
466     # Anything else
467 wakaba 1.55 my $token = {type => CHARACTER_TOKEN,
468 wakaba 1.112 data => chr $self->{next_char},
469 wakaba 1.120 line => $self->{line}, column => $self->{column},
470 wakaba 1.118 };
471 wakaba 1.1 ## Stay in the data state
472     !!!next-input-character;
473    
474     !!!emit ($token);
475    
476     redo A;
477 wakaba 1.57 } elsif ($self->{state} == ENTITY_DATA_STATE) {
478 wakaba 1.1 ## (cannot happen in CDATA state)
479 wakaba 1.112
480 wakaba 1.120 my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
481 wakaba 1.1
482 wakaba 1.72 my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);
483 wakaba 1.1
484 wakaba 1.57 $self->{state} = DATA_STATE;
485 wakaba 1.1 # next-input-character is already done
486    
487     unless (defined $token) {
488 wakaba 1.77 !!!cp (13);
489 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '&',
490 wakaba 1.120 line => $l, column => $c,
491 wakaba 1.118 });
492 wakaba 1.1 } else {
493 wakaba 1.77 !!!cp (14);
494 wakaba 1.1 !!!emit ($token);
495     }
496    
497     redo A;
498 wakaba 1.57 } elsif ($self->{state} == TAG_OPEN_STATE) {
499 wakaba 1.40 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
500 wakaba 1.76 if ($self->{next_char} == 0x002F) { # /
501 wakaba 1.77 !!!cp (15);
502 wakaba 1.1 !!!next-input-character;
503 wakaba 1.57 $self->{state} = CLOSE_TAG_OPEN_STATE;
504 wakaba 1.1 redo A;
505     } else {
506 wakaba 1.77 !!!cp (16);
507 wakaba 1.1 ## reconsume
508 wakaba 1.57 $self->{state} = DATA_STATE;
509 wakaba 1.1
510 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '<',
511 wakaba 1.120 line => $self->{line_prev},
512     column => $self->{column_prev},
513 wakaba 1.118 });
514 wakaba 1.1
515     redo A;
516     }
517 wakaba 1.40 } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
518 wakaba 1.76 if ($self->{next_char} == 0x0021) { # !
519 wakaba 1.77 !!!cp (17);
520 wakaba 1.57 $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
521 wakaba 1.1 !!!next-input-character;
522     redo A;
523 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
524 wakaba 1.77 !!!cp (18);
525 wakaba 1.57 $self->{state} = CLOSE_TAG_OPEN_STATE;
526 wakaba 1.1 !!!next-input-character;
527     redo A;
528 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
529     $self->{next_char} <= 0x005A) { # A..Z
530 wakaba 1.77 !!!cp (19);
531 wakaba 1.1 $self->{current_token}
532 wakaba 1.55 = {type => START_TAG_TOKEN,
533 wakaba 1.112 tag_name => chr ($self->{next_char} + 0x0020),
534     line => $self->{line_prev},
535     column => $self->{column_prev}};
536 wakaba 1.57 $self->{state} = TAG_NAME_STATE;
537 wakaba 1.1 !!!next-input-character;
538     redo A;
539 wakaba 1.76 } elsif (0x0061 <= $self->{next_char} and
540     $self->{next_char} <= 0x007A) { # a..z
541 wakaba 1.77 !!!cp (20);
542 wakaba 1.55 $self->{current_token} = {type => START_TAG_TOKEN,
543 wakaba 1.112 tag_name => chr ($self->{next_char}),
544     line => $self->{line_prev},
545     column => $self->{column_prev}};
546 wakaba 1.57 $self->{state} = TAG_NAME_STATE;
547 wakaba 1.1 !!!next-input-character;
548     redo A;
549 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
550 wakaba 1.77 !!!cp (21);
551 wakaba 1.115 !!!parse-error (type => 'empty start tag',
552     line => $self->{line_prev},
553     column => $self->{column_prev});
554 wakaba 1.57 $self->{state} = DATA_STATE;
555 wakaba 1.1 !!!next-input-character;
556    
557 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '<>',
558 wakaba 1.120 line => $self->{line_prev},
559     column => $self->{column_prev},
560 wakaba 1.118 });
561 wakaba 1.1
562     redo A;
563 wakaba 1.76 } elsif ($self->{next_char} == 0x003F) { # ?
564 wakaba 1.77 !!!cp (22);
565 wakaba 1.115 !!!parse-error (type => 'pio',
566     line => $self->{line_prev},
567     column => $self->{column_prev});
568 wakaba 1.57 $self->{state} = BOGUS_COMMENT_STATE;
569 wakaba 1.112 $self->{current_token} = {type => COMMENT_TOKEN, data => '',
570 wakaba 1.120 line => $self->{line_prev},
571     column => $self->{column_prev},
572 wakaba 1.118 };
573 wakaba 1.76 ## $self->{next_char} is intentionally left as is
574 wakaba 1.1 redo A;
575     } else {
576 wakaba 1.77 !!!cp (23);
577 wakaba 1.3 !!!parse-error (type => 'bare stago');
578 wakaba 1.57 $self->{state} = DATA_STATE;
579 wakaba 1.1 ## reconsume
580    
581 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '<',
582 wakaba 1.120 line => $self->{line_prev},
583     column => $self->{column_prev},
584 wakaba 1.118 });
585 wakaba 1.1
586     redo A;
587     }
588     } else {
589 wakaba 1.40 die "$0: $self->{content_model} in tag open";
590 wakaba 1.1 }
591 wakaba 1.57 } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
592 wakaba 1.113 my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
593 wakaba 1.40 if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
594 wakaba 1.23 if (defined $self->{last_emitted_start_tag_name}) {
595 wakaba 1.112
596 wakaba 1.30 ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>
597 wakaba 1.23 my @next_char;
598     TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {
599 wakaba 1.76 push @next_char, $self->{next_char};
600 wakaba 1.23 my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);
601     my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;
602 wakaba 1.76 if ($self->{next_char} == $c or $self->{next_char} == $C) {
603 wakaba 1.77 !!!cp (24);
604 wakaba 1.23 !!!next-input-character;
605     next TAGNAME;
606     } else {
607 wakaba 1.77 !!!cp (25);
608 wakaba 1.76 $self->{next_char} = shift @next_char; # reconsume
609 wakaba 1.23 !!!back-next-input-character (@next_char);
610 wakaba 1.57 $self->{state} = DATA_STATE;
611 wakaba 1.23
612 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '</',
613 wakaba 1.120 line => $l, column => $c,
614 wakaba 1.118 });
615 wakaba 1.23
616     redo A;
617     }
618     }
619 wakaba 1.76 push @next_char, $self->{next_char};
620 wakaba 1.23
621 wakaba 1.76 unless ($self->{next_char} == 0x0009 or # HT
622     $self->{next_char} == 0x000A or # LF
623     $self->{next_char} == 0x000B or # VT
624     $self->{next_char} == 0x000C or # FF
625     $self->{next_char} == 0x0020 or # SP
626     $self->{next_char} == 0x003E or # >
627     $self->{next_char} == 0x002F or # /
628     $self->{next_char} == -1) {
629 wakaba 1.77 !!!cp (26);
630 wakaba 1.76 $self->{next_char} = shift @next_char; # reconsume
631 wakaba 1.1 !!!back-next-input-character (@next_char);
632 wakaba 1.57 $self->{state} = DATA_STATE;
633 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '</',
634 wakaba 1.120 line => $l, column => $c,
635 wakaba 1.118 });
636 wakaba 1.1 redo A;
637 wakaba 1.23 } else {
638 wakaba 1.77 !!!cp (27);
639 wakaba 1.76 $self->{next_char} = shift @next_char;
640 wakaba 1.23 !!!back-next-input-character (@next_char);
641     # and consume...
642 wakaba 1.1 }
643 wakaba 1.23 } else {
644     ## No start tag token has ever been emitted
645 wakaba 1.77 !!!cp (28);
646 wakaba 1.23 # next-input-character is already done
647 wakaba 1.57 $self->{state} = DATA_STATE;
648 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '</',
649 wakaba 1.120 line => $l, column => $c,
650 wakaba 1.118 });
651 wakaba 1.1 redo A;
652     }
653     }
654    
655 wakaba 1.76 if (0x0041 <= $self->{next_char} and
656     $self->{next_char} <= 0x005A) { # A..Z
657 wakaba 1.77 !!!cp (29);
658 wakaba 1.112 $self->{current_token}
659     = {type => END_TAG_TOKEN,
660     tag_name => chr ($self->{next_char} + 0x0020),
661     line => $l, column => $c};
662 wakaba 1.57 $self->{state} = TAG_NAME_STATE;
663 wakaba 1.1 !!!next-input-character;
664     redo A;
665 wakaba 1.76 } elsif (0x0061 <= $self->{next_char} and
666     $self->{next_char} <= 0x007A) { # a..z
667 wakaba 1.77 !!!cp (30);
668 wakaba 1.55 $self->{current_token} = {type => END_TAG_TOKEN,
669 wakaba 1.112 tag_name => chr ($self->{next_char}),
670     line => $l, column => $c};
671 wakaba 1.57 $self->{state} = TAG_NAME_STATE;
672 wakaba 1.1 !!!next-input-character;
673     redo A;
674 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
675 wakaba 1.77 !!!cp (31);
676 wakaba 1.115 !!!parse-error (type => 'empty end tag',
677     line => $self->{line_prev}, ## "<" in "</>"
678     column => $self->{column_prev} - 1);
679 wakaba 1.57 $self->{state} = DATA_STATE;
680 wakaba 1.1 !!!next-input-character;
681     redo A;
682 wakaba 1.76 } elsif ($self->{next_char} == -1) {
683 wakaba 1.77 !!!cp (32);
684 wakaba 1.3 !!!parse-error (type => 'bare etago');
685 wakaba 1.57 $self->{state} = DATA_STATE;
686 wakaba 1.1 # reconsume
687    
688 wakaba 1.112 !!!emit ({type => CHARACTER_TOKEN, data => '</',
689 wakaba 1.120 line => $l, column => $c,
690 wakaba 1.118 });
691 wakaba 1.1
692     redo A;
693     } else {
694 wakaba 1.77 !!!cp (33);
695 wakaba 1.3 !!!parse-error (type => 'bogus end tag');
696 wakaba 1.57 $self->{state} = BOGUS_COMMENT_STATE;
697 wakaba 1.112 $self->{current_token} = {type => COMMENT_TOKEN, data => '',
698 wakaba 1.120 line => $self->{line_prev}, # "<" of "</"
699     column => $self->{column_prev} - 1,
700 wakaba 1.118 };
701 wakaba 1.76 ## $self->{next_char} is intentionally left as is
702 wakaba 1.1 redo A;
703     }
704 wakaba 1.57 } elsif ($self->{state} == TAG_NAME_STATE) {
705 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
706     $self->{next_char} == 0x000A or # LF
707     $self->{next_char} == 0x000B or # VT
708     $self->{next_char} == 0x000C or # FF
709     $self->{next_char} == 0x0020) { # SP
710 wakaba 1.77 !!!cp (34);
711 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
712 wakaba 1.1 !!!next-input-character;
713     redo A;
714 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
715 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
716 wakaba 1.77 !!!cp (35);
717 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
718 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
719 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
720 wakaba 1.78 #if ($self->{current_token}->{attributes}) {
721     # ## NOTE: This should never be reached.
722     # !!! cp (36);
723     # !!! parse-error (type => 'end tag attribute');
724     #} else {
725 wakaba 1.77 !!!cp (37);
726 wakaba 1.78 #}
727 wakaba 1.1 } else {
728     die "$0: $self->{current_token}->{type}: Unknown token type";
729     }
730 wakaba 1.57 $self->{state} = DATA_STATE;
731 wakaba 1.1 !!!next-input-character;
732    
733     !!!emit ($self->{current_token}); # start tag or end tag
734    
735     redo A;
736 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
737     $self->{next_char} <= 0x005A) { # A..Z
738 wakaba 1.77 !!!cp (38);
739 wakaba 1.76 $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);
740 wakaba 1.1 # start tag or end tag
741     ## Stay in this state
742     !!!next-input-character;
743     redo A;
744 wakaba 1.76 } elsif ($self->{next_char} == -1) {
745 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
746 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
747 wakaba 1.77 !!!cp (39);
748 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
749 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
750 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
751 wakaba 1.78 #if ($self->{current_token}->{attributes}) {
752     # ## NOTE: This state should never be reached.
753     # !!! cp (40);
754     # !!! parse-error (type => 'end tag attribute');
755     #} else {
756 wakaba 1.77 !!!cp (41);
757 wakaba 1.78 #}
758 wakaba 1.1 } else {
759     die "$0: $self->{current_token}->{type}: Unknown token type";
760     }
761 wakaba 1.57 $self->{state} = DATA_STATE;
762 wakaba 1.1 # reconsume
763    
764     !!!emit ($self->{current_token}); # start tag or end tag
765    
766     redo A;
767 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
768 wakaba 1.1 !!!next-input-character;
769 wakaba 1.76 if ($self->{next_char} == 0x003E and # >
770 wakaba 1.55 $self->{current_token}->{type} == START_TAG_TOKEN and
771 wakaba 1.1 $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
772     # permitted slash
773 wakaba 1.77 !!!cp (42);
774 wakaba 1.1 #
775     } else {
776 wakaba 1.77 !!!cp (43);
777 wakaba 1.3 !!!parse-error (type => 'nestc');
778 wakaba 1.1 }
779 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
780 wakaba 1.1 # next-input-character is already done
781     redo A;
782     } else {
783 wakaba 1.77 !!!cp (44);
784 wakaba 1.76 $self->{current_token}->{tag_name} .= chr $self->{next_char};
785 wakaba 1.1 # start tag or end tag
786     ## Stay in the state
787     !!!next-input-character;
788     redo A;
789     }
790 wakaba 1.57 } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
791 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
792     $self->{next_char} == 0x000A or # LF
793     $self->{next_char} == 0x000B or # VT
794     $self->{next_char} == 0x000C or # FF
795     $self->{next_char} == 0x0020) { # SP
796 wakaba 1.77 !!!cp (45);
797 wakaba 1.1 ## Stay in the state
798     !!!next-input-character;
799     redo A;
800 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
801 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
802 wakaba 1.77 !!!cp (46);
803 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
804 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
805 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
806 wakaba 1.1 if ($self->{current_token}->{attributes}) {
807 wakaba 1.77 !!!cp (47);
808 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
809 wakaba 1.77 } else {
810     !!!cp (48);
811 wakaba 1.1 }
812     } else {
813     die "$0: $self->{current_token}->{type}: Unknown token type";
814     }
815 wakaba 1.57 $self->{state} = DATA_STATE;
816 wakaba 1.1 !!!next-input-character;
817    
818     !!!emit ($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 !!!cp (49);
824 wakaba 1.119 $self->{current_attribute}
825     = {name => chr ($self->{next_char} + 0x0020),
826     value => '',
827     line => $self->{line}, column => $self->{column}};
828 wakaba 1.57 $self->{state} = ATTRIBUTE_NAME_STATE;
829 wakaba 1.1 !!!next-input-character;
830     redo A;
831 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
832 wakaba 1.1 !!!next-input-character;
833 wakaba 1.76 if ($self->{next_char} == 0x003E and # >
834 wakaba 1.55 $self->{current_token}->{type} == START_TAG_TOKEN and
835 wakaba 1.1 $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
836     # permitted slash
837 wakaba 1.77 !!!cp (50);
838 wakaba 1.1 #
839     } else {
840 wakaba 1.77 !!!cp (51);
841 wakaba 1.3 !!!parse-error (type => 'nestc');
842 wakaba 1.1 }
843     ## Stay in the state
844     # next-input-character is already done
845     redo A;
846 wakaba 1.76 } elsif ($self->{next_char} == -1) {
847 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
848 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
849 wakaba 1.77 !!!cp (52);
850 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
851 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
852 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
853 wakaba 1.1 if ($self->{current_token}->{attributes}) {
854 wakaba 1.77 !!!cp (53);
855 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
856 wakaba 1.77 } else {
857     !!!cp (54);
858 wakaba 1.1 }
859     } else {
860     die "$0: $self->{current_token}->{type}: Unknown token type";
861     }
862 wakaba 1.57 $self->{state} = DATA_STATE;
863 wakaba 1.1 # reconsume
864    
865     !!!emit ($self->{current_token}); # start tag or end tag
866    
867     redo A;
868     } else {
869 wakaba 1.72 if ({
870     0x0022 => 1, # "
871     0x0027 => 1, # '
872     0x003D => 1, # =
873 wakaba 1.76 }->{$self->{next_char}}) {
874 wakaba 1.77 !!!cp (55);
875 wakaba 1.72 !!!parse-error (type => 'bad attribute name');
876 wakaba 1.77 } else {
877     !!!cp (56);
878 wakaba 1.72 }
879 wakaba 1.119 $self->{current_attribute}
880     = {name => chr ($self->{next_char}),
881     value => '',
882     line => $self->{line}, column => $self->{column}};
883 wakaba 1.57 $self->{state} = ATTRIBUTE_NAME_STATE;
884 wakaba 1.1 !!!next-input-character;
885     redo A;
886     }
887 wakaba 1.57 } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
888 wakaba 1.1 my $before_leave = sub {
889     if (exists $self->{current_token}->{attributes} # start tag or end tag
890     ->{$self->{current_attribute}->{name}}) { # MUST
891 wakaba 1.77 !!!cp (57);
892 wakaba 1.120 !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});
893 wakaba 1.1 ## Discard $self->{current_attribute} # MUST
894     } else {
895 wakaba 1.77 !!!cp (58);
896 wakaba 1.1 $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}
897     = $self->{current_attribute};
898     }
899     }; # $before_leave
900    
901 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
902     $self->{next_char} == 0x000A or # LF
903     $self->{next_char} == 0x000B or # VT
904     $self->{next_char} == 0x000C or # FF
905     $self->{next_char} == 0x0020) { # SP
906 wakaba 1.77 !!!cp (59);
907 wakaba 1.1 $before_leave->();
908 wakaba 1.57 $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
909 wakaba 1.1 !!!next-input-character;
910     redo A;
911 wakaba 1.76 } elsif ($self->{next_char} == 0x003D) { # =
912 wakaba 1.77 !!!cp (60);
913 wakaba 1.1 $before_leave->();
914 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
915 wakaba 1.1 !!!next-input-character;
916     redo A;
917 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
918 wakaba 1.1 $before_leave->();
919 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
920 wakaba 1.77 !!!cp (61);
921 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
922 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
923 wakaba 1.77 !!!cp (62);
924 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
925 wakaba 1.1 if ($self->{current_token}->{attributes}) {
926 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
927 wakaba 1.1 }
928     } else {
929     die "$0: $self->{current_token}->{type}: Unknown token type";
930     }
931 wakaba 1.57 $self->{state} = DATA_STATE;
932 wakaba 1.1 !!!next-input-character;
933    
934     !!!emit ($self->{current_token}); # start tag or end tag
935    
936     redo A;
937 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
938     $self->{next_char} <= 0x005A) { # A..Z
939 wakaba 1.77 !!!cp (63);
940 wakaba 1.76 $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);
941 wakaba 1.1 ## Stay in the state
942     !!!next-input-character;
943     redo A;
944 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
945 wakaba 1.1 $before_leave->();
946     !!!next-input-character;
947 wakaba 1.76 if ($self->{next_char} == 0x003E and # >
948 wakaba 1.55 $self->{current_token}->{type} == START_TAG_TOKEN and
949 wakaba 1.1 $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
950     # permitted slash
951 wakaba 1.77 !!!cp (64);
952 wakaba 1.1 #
953     } else {
954 wakaba 1.77 !!!cp (65);
955 wakaba 1.3 !!!parse-error (type => 'nestc');
956 wakaba 1.1 }
957 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
958 wakaba 1.1 # next-input-character is already done
959     redo A;
960 wakaba 1.76 } elsif ($self->{next_char} == -1) {
961 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
962 wakaba 1.1 $before_leave->();
963 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
964 wakaba 1.77 !!!cp (66);
965 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
966 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
967 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
968 wakaba 1.1 if ($self->{current_token}->{attributes}) {
969 wakaba 1.77 !!!cp (67);
970 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
971 wakaba 1.77 } else {
972 wakaba 1.78 ## NOTE: This state should never be reached.
973 wakaba 1.77 !!!cp (68);
974 wakaba 1.1 }
975     } else {
976     die "$0: $self->{current_token}->{type}: Unknown token type";
977     }
978 wakaba 1.57 $self->{state} = DATA_STATE;
979 wakaba 1.1 # reconsume
980    
981     !!!emit ($self->{current_token}); # start tag or end tag
982    
983     redo A;
984     } else {
985 wakaba 1.76 if ($self->{next_char} == 0x0022 or # "
986     $self->{next_char} == 0x0027) { # '
987 wakaba 1.77 !!!cp (69);
988 wakaba 1.72 !!!parse-error (type => 'bad attribute name');
989 wakaba 1.77 } else {
990     !!!cp (70);
991 wakaba 1.72 }
992 wakaba 1.76 $self->{current_attribute}->{name} .= chr ($self->{next_char});
993 wakaba 1.1 ## Stay in the state
994     !!!next-input-character;
995     redo A;
996     }
997 wakaba 1.57 } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
998 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
999     $self->{next_char} == 0x000A or # LF
1000     $self->{next_char} == 0x000B or # VT
1001     $self->{next_char} == 0x000C or # FF
1002     $self->{next_char} == 0x0020) { # SP
1003 wakaba 1.77 !!!cp (71);
1004 wakaba 1.1 ## Stay in the state
1005     !!!next-input-character;
1006     redo A;
1007 wakaba 1.76 } elsif ($self->{next_char} == 0x003D) { # =
1008 wakaba 1.77 !!!cp (72);
1009 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1010 wakaba 1.1 !!!next-input-character;
1011     redo A;
1012 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1013 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1014 wakaba 1.77 !!!cp (73);
1015 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1016 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1017 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1018 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1019 wakaba 1.77 !!!cp (74);
1020 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1021 wakaba 1.77 } else {
1022 wakaba 1.78 ## NOTE: This state should never be reached.
1023 wakaba 1.77 !!!cp (75);
1024 wakaba 1.1 }
1025     } else {
1026     die "$0: $self->{current_token}->{type}: Unknown token type";
1027     }
1028 wakaba 1.57 $self->{state} = DATA_STATE;
1029 wakaba 1.1 !!!next-input-character;
1030    
1031     !!!emit ($self->{current_token}); # start tag or end tag
1032    
1033     redo A;
1034 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
1035     $self->{next_char} <= 0x005A) { # A..Z
1036 wakaba 1.77 !!!cp (76);
1037 wakaba 1.119 $self->{current_attribute}
1038     = {name => chr ($self->{next_char} + 0x0020),
1039     value => '',
1040     line => $self->{line}, column => $self->{column}};
1041 wakaba 1.57 $self->{state} = ATTRIBUTE_NAME_STATE;
1042 wakaba 1.1 !!!next-input-character;
1043     redo A;
1044 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
1045 wakaba 1.1 !!!next-input-character;
1046 wakaba 1.76 if ($self->{next_char} == 0x003E and # >
1047 wakaba 1.55 $self->{current_token}->{type} == START_TAG_TOKEN and
1048 wakaba 1.1 $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
1049     # permitted slash
1050 wakaba 1.77 !!!cp (77);
1051 wakaba 1.1 #
1052     } else {
1053 wakaba 1.77 !!!cp (78);
1054 wakaba 1.3 !!!parse-error (type => 'nestc');
1055 wakaba 1.33 ## TODO: Different error type for <aa / bb> than <aa/>
1056 wakaba 1.1 }
1057 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1058 wakaba 1.1 # next-input-character is already done
1059     redo A;
1060 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1061 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1062 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1063 wakaba 1.77 !!!cp (79);
1064 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1065 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1066 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1067 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1068 wakaba 1.77 !!!cp (80);
1069 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1070 wakaba 1.77 } else {
1071 wakaba 1.78 ## NOTE: This state should never be reached.
1072 wakaba 1.77 !!!cp (81);
1073 wakaba 1.1 }
1074     } else {
1075     die "$0: $self->{current_token}->{type}: Unknown token type";
1076     }
1077 wakaba 1.57 $self->{state} = DATA_STATE;
1078 wakaba 1.1 # reconsume
1079    
1080     !!!emit ($self->{current_token}); # start tag or end tag
1081    
1082     redo A;
1083     } else {
1084 wakaba 1.77 !!!cp (82);
1085 wakaba 1.119 $self->{current_attribute}
1086     = {name => chr ($self->{next_char}),
1087     value => '',
1088     line => $self->{line}, column => $self->{column}};
1089 wakaba 1.57 $self->{state} = ATTRIBUTE_NAME_STATE;
1090 wakaba 1.1 !!!next-input-character;
1091     redo A;
1092     }
1093 wakaba 1.57 } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1094 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1095     $self->{next_char} == 0x000A or # LF
1096     $self->{next_char} == 0x000B or # VT
1097     $self->{next_char} == 0x000C or # FF
1098     $self->{next_char} == 0x0020) { # SP
1099 wakaba 1.77 !!!cp (83);
1100 wakaba 1.1 ## Stay in the state
1101     !!!next-input-character;
1102     redo A;
1103 wakaba 1.76 } elsif ($self->{next_char} == 0x0022) { # "
1104 wakaba 1.77 !!!cp (84);
1105 wakaba 1.57 $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1106 wakaba 1.1 !!!next-input-character;
1107     redo A;
1108 wakaba 1.76 } elsif ($self->{next_char} == 0x0026) { # &
1109 wakaba 1.77 !!!cp (85);
1110 wakaba 1.57 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1111 wakaba 1.1 ## reconsume
1112     redo A;
1113 wakaba 1.76 } elsif ($self->{next_char} == 0x0027) { # '
1114 wakaba 1.77 !!!cp (86);
1115 wakaba 1.57 $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1116 wakaba 1.1 !!!next-input-character;
1117     redo A;
1118 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1119 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1120 wakaba 1.77 !!!cp (87);
1121 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1122 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1123 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1124 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1125 wakaba 1.77 !!!cp (88);
1126 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1127 wakaba 1.77 } else {
1128 wakaba 1.78 ## NOTE: This state should never be reached.
1129 wakaba 1.77 !!!cp (89);
1130 wakaba 1.1 }
1131     } else {
1132     die "$0: $self->{current_token}->{type}: Unknown token type";
1133     }
1134 wakaba 1.57 $self->{state} = DATA_STATE;
1135 wakaba 1.1 !!!next-input-character;
1136    
1137     !!!emit ($self->{current_token}); # start tag or end tag
1138    
1139     redo A;
1140 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1141 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1142 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1143 wakaba 1.77 !!!cp (90);
1144 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1145 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1146 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1147 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1148 wakaba 1.77 !!!cp (91);
1149 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1150 wakaba 1.77 } else {
1151 wakaba 1.78 ## NOTE: This state should never be reached.
1152 wakaba 1.77 !!!cp (92);
1153 wakaba 1.1 }
1154     } else {
1155     die "$0: $self->{current_token}->{type}: Unknown token type";
1156     }
1157 wakaba 1.57 $self->{state} = DATA_STATE;
1158 wakaba 1.1 ## reconsume
1159    
1160     !!!emit ($self->{current_token}); # start tag or end tag
1161    
1162     redo A;
1163     } else {
1164 wakaba 1.76 if ($self->{next_char} == 0x003D) { # =
1165 wakaba 1.77 !!!cp (93);
1166 wakaba 1.72 !!!parse-error (type => 'bad attribute value');
1167 wakaba 1.77 } else {
1168     !!!cp (94);
1169 wakaba 1.72 }
1170 wakaba 1.76 $self->{current_attribute}->{value} .= chr ($self->{next_char});
1171 wakaba 1.57 $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1172 wakaba 1.1 !!!next-input-character;
1173     redo A;
1174     }
1175 wakaba 1.57 } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1176 wakaba 1.76 if ($self->{next_char} == 0x0022) { # "
1177 wakaba 1.77 !!!cp (95);
1178 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1179 wakaba 1.1 !!!next-input-character;
1180     redo A;
1181 wakaba 1.76 } elsif ($self->{next_char} == 0x0026) { # &
1182 wakaba 1.77 !!!cp (96);
1183 wakaba 1.57 $self->{last_attribute_value_state} = $self->{state};
1184     $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1185 wakaba 1.1 !!!next-input-character;
1186     redo A;
1187 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1188 wakaba 1.3 !!!parse-error (type => 'unclosed attribute value');
1189 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1190 wakaba 1.77 !!!cp (97);
1191 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1192 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1193 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1194 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1195 wakaba 1.77 !!!cp (98);
1196 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1197 wakaba 1.77 } else {
1198 wakaba 1.78 ## NOTE: This state should never be reached.
1199 wakaba 1.77 !!!cp (99);
1200 wakaba 1.1 }
1201     } else {
1202     die "$0: $self->{current_token}->{type}: Unknown token type";
1203     }
1204 wakaba 1.57 $self->{state} = DATA_STATE;
1205 wakaba 1.1 ## reconsume
1206    
1207     !!!emit ($self->{current_token}); # start tag or end tag
1208    
1209     redo A;
1210     } else {
1211 wakaba 1.77 !!!cp (100);
1212 wakaba 1.76 $self->{current_attribute}->{value} .= chr ($self->{next_char});
1213 wakaba 1.1 ## Stay in the state
1214     !!!next-input-character;
1215     redo A;
1216     }
1217 wakaba 1.57 } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1218 wakaba 1.76 if ($self->{next_char} == 0x0027) { # '
1219 wakaba 1.77 !!!cp (101);
1220 wakaba 1.72 $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1221 wakaba 1.1 !!!next-input-character;
1222     redo A;
1223 wakaba 1.76 } elsif ($self->{next_char} == 0x0026) { # &
1224 wakaba 1.77 !!!cp (102);
1225 wakaba 1.57 $self->{last_attribute_value_state} = $self->{state};
1226     $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1227 wakaba 1.1 !!!next-input-character;
1228     redo A;
1229 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1230 wakaba 1.3 !!!parse-error (type => 'unclosed attribute value');
1231 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1232 wakaba 1.77 !!!cp (103);
1233 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1234 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1235 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1236 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1237 wakaba 1.77 !!!cp (104);
1238 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1239 wakaba 1.77 } else {
1240 wakaba 1.78 ## NOTE: This state should never be reached.
1241 wakaba 1.77 !!!cp (105);
1242 wakaba 1.1 }
1243     } else {
1244     die "$0: $self->{current_token}->{type}: Unknown token type";
1245     }
1246 wakaba 1.57 $self->{state} = DATA_STATE;
1247 wakaba 1.1 ## reconsume
1248    
1249     !!!emit ($self->{current_token}); # start tag or end tag
1250    
1251     redo A;
1252     } else {
1253 wakaba 1.77 !!!cp (106);
1254 wakaba 1.76 $self->{current_attribute}->{value} .= chr ($self->{next_char});
1255 wakaba 1.1 ## Stay in the state
1256     !!!next-input-character;
1257     redo A;
1258     }
1259 wakaba 1.57 } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1260 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1261     $self->{next_char} == 0x000A or # LF
1262     $self->{next_char} == 0x000B or # HT
1263     $self->{next_char} == 0x000C or # FF
1264     $self->{next_char} == 0x0020) { # SP
1265 wakaba 1.77 !!!cp (107);
1266 wakaba 1.57 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1267 wakaba 1.1 !!!next-input-character;
1268     redo A;
1269 wakaba 1.76 } elsif ($self->{next_char} == 0x0026) { # &
1270 wakaba 1.77 !!!cp (108);
1271 wakaba 1.57 $self->{last_attribute_value_state} = $self->{state};
1272     $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1273 wakaba 1.1 !!!next-input-character;
1274     redo A;
1275 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1276 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1277 wakaba 1.77 !!!cp (109);
1278 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1279 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1280 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1281 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1282 wakaba 1.77 !!!cp (110);
1283 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1284 wakaba 1.77 } else {
1285 wakaba 1.78 ## NOTE: This state should never be reached.
1286 wakaba 1.77 !!!cp (111);
1287 wakaba 1.1 }
1288     } else {
1289     die "$0: $self->{current_token}->{type}: Unknown token type";
1290     }
1291 wakaba 1.57 $self->{state} = DATA_STATE;
1292 wakaba 1.1 !!!next-input-character;
1293    
1294     !!!emit ($self->{current_token}); # start tag or end tag
1295    
1296     redo A;
1297 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1298 wakaba 1.3 !!!parse-error (type => 'unclosed tag');
1299 wakaba 1.55 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1300 wakaba 1.77 !!!cp (112);
1301 wakaba 1.1 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1302 wakaba 1.55 } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1303 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1304 wakaba 1.1 if ($self->{current_token}->{attributes}) {
1305 wakaba 1.77 !!!cp (113);
1306 wakaba 1.3 !!!parse-error (type => 'end tag attribute');
1307 wakaba 1.77 } else {
1308 wakaba 1.78 ## NOTE: This state should never be reached.
1309 wakaba 1.77 !!!cp (114);
1310 wakaba 1.1 }
1311     } else {
1312     die "$0: $self->{current_token}->{type}: Unknown token type";
1313     }
1314 wakaba 1.57 $self->{state} = DATA_STATE;
1315 wakaba 1.1 ## reconsume
1316    
1317     !!!emit ($self->{current_token}); # start tag or end tag
1318    
1319     redo A;
1320     } else {
1321 wakaba 1.72 if ({
1322     0x0022 => 1, # "
1323     0x0027 => 1, # '
1324     0x003D => 1, # =
1325 wakaba 1.76 }->{$self->{next_char}}) {
1326 wakaba 1.77 !!!cp (115);
1327 wakaba 1.72 !!!parse-error (type => 'bad attribute value');
1328 wakaba 1.77 } else {
1329     !!!cp (116);
1330 wakaba 1.72 }
1331 wakaba 1.76 $self->{current_attribute}->{value} .= chr ($self->{next_char});
1332 wakaba 1.1 ## Stay in the state
1333     !!!next-input-character;
1334     redo A;
1335     }
1336 wakaba 1.57 } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {
1337 wakaba 1.72 my $token = $self->_tokenize_attempt_to_consume_an_entity
1338     (1,
1339     $self->{last_attribute_value_state}
1340     == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "
1341     $self->{last_attribute_value_state}
1342     == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '
1343     -1);
1344 wakaba 1.1
1345     unless (defined $token) {
1346 wakaba 1.77 !!!cp (117);
1347 wakaba 1.1 $self->{current_attribute}->{value} .= '&';
1348     } else {
1349 wakaba 1.77 !!!cp (118);
1350 wakaba 1.1 $self->{current_attribute}->{value} .= $token->{data};
1351 wakaba 1.66 $self->{current_attribute}->{has_reference} = $token->{has_reference};
1352 wakaba 1.1 ## ISSUE: spec says "append the returned character token to the current attribute's value"
1353     }
1354    
1355     $self->{state} = $self->{last_attribute_value_state};
1356     # next-input-character is already done
1357     redo A;
1358 wakaba 1.72 } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
1359 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1360     $self->{next_char} == 0x000A or # LF
1361     $self->{next_char} == 0x000B or # VT
1362     $self->{next_char} == 0x000C or # FF
1363     $self->{next_char} == 0x0020) { # SP
1364 wakaba 1.77 !!!cp (118);
1365 wakaba 1.72 $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1366     !!!next-input-character;
1367     redo A;
1368 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1369 wakaba 1.72 if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1370 wakaba 1.77 !!!cp (119);
1371 wakaba 1.72 $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1372     } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1373     $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1374     if ($self->{current_token}->{attributes}) {
1375 wakaba 1.77 !!!cp (120);
1376 wakaba 1.72 !!!parse-error (type => 'end tag attribute');
1377 wakaba 1.77 } else {
1378 wakaba 1.78 ## NOTE: This state should never be reached.
1379 wakaba 1.77 !!!cp (121);
1380 wakaba 1.72 }
1381     } else {
1382     die "$0: $self->{current_token}->{type}: Unknown token type";
1383     }
1384     $self->{state} = DATA_STATE;
1385     !!!next-input-character;
1386    
1387     !!!emit ($self->{current_token}); # start tag or end tag
1388    
1389     redo A;
1390 wakaba 1.76 } elsif ($self->{next_char} == 0x002F) { # /
1391 wakaba 1.72 !!!next-input-character;
1392 wakaba 1.76 if ($self->{next_char} == 0x003E and # >
1393 wakaba 1.72 $self->{current_token}->{type} == START_TAG_TOKEN and
1394     $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
1395     # permitted slash
1396 wakaba 1.77 !!!cp (122);
1397 wakaba 1.72 #
1398     } else {
1399 wakaba 1.77 !!!cp (123);
1400 wakaba 1.72 !!!parse-error (type => 'nestc');
1401     }
1402     $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1403     # next-input-character is already done
1404     redo A;
1405     } else {
1406 wakaba 1.77 !!!cp (124);
1407 wakaba 1.72 !!!parse-error (type => 'no space between attributes');
1408     $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1409     ## reconsume
1410     redo A;
1411     }
1412 wakaba 1.57 } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
1413 wakaba 1.1 ## (only happen if PCDATA state)
1414    
1415 wakaba 1.112 ## NOTE: Set by the previous state
1416     #my $token = {type => COMMENT_TOKEN, data => ''};
1417 wakaba 1.1
1418     BC: {
1419 wakaba 1.76 if ($self->{next_char} == 0x003E) { # >
1420 wakaba 1.77 !!!cp (124);
1421 wakaba 1.57 $self->{state} = DATA_STATE;
1422 wakaba 1.1 !!!next-input-character;
1423    
1424 wakaba 1.112 !!!emit ($self->{current_token}); # comment
1425 wakaba 1.1
1426     redo A;
1427 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1428 wakaba 1.77 !!!cp (125);
1429 wakaba 1.57 $self->{state} = DATA_STATE;
1430 wakaba 1.1 ## reconsume
1431    
1432 wakaba 1.112 !!!emit ($self->{current_token}); # comment
1433 wakaba 1.1
1434     redo A;
1435     } else {
1436 wakaba 1.77 !!!cp (126);
1437 wakaba 1.112 $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
1438 wakaba 1.1 !!!next-input-character;
1439     redo BC;
1440     }
1441     } # BC
1442 wakaba 1.77
1443     die "$0: _get_next_token: unexpected case [BC]";
1444 wakaba 1.57 } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
1445 wakaba 1.1 ## (only happen if PCDATA state)
1446    
1447 wakaba 1.120 my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);
1448 wakaba 1.112
1449 wakaba 1.1 my @next_char;
1450 wakaba 1.76 push @next_char, $self->{next_char};
1451 wakaba 1.1
1452 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
1453 wakaba 1.1 !!!next-input-character;
1454 wakaba 1.76 push @next_char, $self->{next_char};
1455     if ($self->{next_char} == 0x002D) { # -
1456 wakaba 1.77 !!!cp (127);
1457 wakaba 1.112 $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1458 wakaba 1.120 line => $l, column => $c,
1459 wakaba 1.118 };
1460 wakaba 1.57 $self->{state} = COMMENT_START_STATE;
1461 wakaba 1.1 !!!next-input-character;
1462     redo A;
1463 wakaba 1.77 } else {
1464     !!!cp (128);
1465 wakaba 1.1 }
1466 wakaba 1.76 } elsif ($self->{next_char} == 0x0044 or # D
1467     $self->{next_char} == 0x0064) { # d
1468 wakaba 1.1 !!!next-input-character;
1469 wakaba 1.76 push @next_char, $self->{next_char};
1470     if ($self->{next_char} == 0x004F or # O
1471     $self->{next_char} == 0x006F) { # o
1472 wakaba 1.1 !!!next-input-character;
1473 wakaba 1.76 push @next_char, $self->{next_char};
1474     if ($self->{next_char} == 0x0043 or # C
1475     $self->{next_char} == 0x0063) { # c
1476 wakaba 1.1 !!!next-input-character;
1477 wakaba 1.76 push @next_char, $self->{next_char};
1478     if ($self->{next_char} == 0x0054 or # T
1479     $self->{next_char} == 0x0074) { # t
1480 wakaba 1.1 !!!next-input-character;
1481 wakaba 1.76 push @next_char, $self->{next_char};
1482     if ($self->{next_char} == 0x0059 or # Y
1483     $self->{next_char} == 0x0079) { # y
1484 wakaba 1.1 !!!next-input-character;
1485 wakaba 1.76 push @next_char, $self->{next_char};
1486     if ($self->{next_char} == 0x0050 or # P
1487     $self->{next_char} == 0x0070) { # p
1488 wakaba 1.1 !!!next-input-character;
1489 wakaba 1.76 push @next_char, $self->{next_char};
1490     if ($self->{next_char} == 0x0045 or # E
1491     $self->{next_char} == 0x0065) { # e
1492 wakaba 1.77 !!!cp (129);
1493     ## TODO: What a stupid code this is!
1494 wakaba 1.57 $self->{state} = DOCTYPE_STATE;
1495 wakaba 1.112 $self->{current_token} = {type => DOCTYPE_TOKEN,
1496     quirks => 1,
1497 wakaba 1.120 line => $l, column => $c,
1498 wakaba 1.118 };
1499 wakaba 1.1 !!!next-input-character;
1500     redo A;
1501 wakaba 1.77 } else {
1502     !!!cp (130);
1503 wakaba 1.1 }
1504 wakaba 1.77 } else {
1505     !!!cp (131);
1506 wakaba 1.1 }
1507 wakaba 1.77 } else {
1508     !!!cp (132);
1509 wakaba 1.1 }
1510 wakaba 1.77 } else {
1511     !!!cp (133);
1512 wakaba 1.1 }
1513 wakaba 1.77 } else {
1514     !!!cp (134);
1515 wakaba 1.1 }
1516 wakaba 1.77 } else {
1517     !!!cp (135);
1518 wakaba 1.1 }
1519 wakaba 1.77 } else {
1520     !!!cp (136);
1521 wakaba 1.1 }
1522    
1523 wakaba 1.30 !!!parse-error (type => 'bogus comment');
1524 wakaba 1.76 $self->{next_char} = shift @next_char;
1525 wakaba 1.1 !!!back-next-input-character (@next_char);
1526 wakaba 1.57 $self->{state} = BOGUS_COMMENT_STATE;
1527 wakaba 1.112 $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1528 wakaba 1.120 line => $l, column => $c,
1529 wakaba 1.118 };
1530 wakaba 1.1 redo A;
1531    
1532     ## ISSUE: typos in spec: chacacters, is is a parse error
1533     ## 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?
1534 wakaba 1.57 } elsif ($self->{state} == COMMENT_START_STATE) {
1535 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
1536 wakaba 1.77 !!!cp (137);
1537 wakaba 1.57 $self->{state} = COMMENT_START_DASH_STATE;
1538 wakaba 1.23 !!!next-input-character;
1539     redo A;
1540 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1541 wakaba 1.77 !!!cp (138);
1542 wakaba 1.23 !!!parse-error (type => 'bogus comment');
1543 wakaba 1.57 $self->{state} = DATA_STATE;
1544 wakaba 1.23 !!!next-input-character;
1545    
1546     !!!emit ($self->{current_token}); # comment
1547    
1548     redo A;
1549 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1550 wakaba 1.77 !!!cp (139);
1551 wakaba 1.23 !!!parse-error (type => 'unclosed comment');
1552 wakaba 1.57 $self->{state} = DATA_STATE;
1553 wakaba 1.23 ## reconsume
1554    
1555     !!!emit ($self->{current_token}); # comment
1556    
1557     redo A;
1558     } else {
1559 wakaba 1.77 !!!cp (140);
1560 wakaba 1.23 $self->{current_token}->{data} # comment
1561 wakaba 1.76 .= chr ($self->{next_char});
1562 wakaba 1.57 $self->{state} = COMMENT_STATE;
1563 wakaba 1.23 !!!next-input-character;
1564     redo A;
1565     }
1566 wakaba 1.57 } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
1567 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
1568 wakaba 1.77 !!!cp (141);
1569 wakaba 1.57 $self->{state} = COMMENT_END_STATE;
1570 wakaba 1.23 !!!next-input-character;
1571     redo A;
1572 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1573 wakaba 1.77 !!!cp (142);
1574 wakaba 1.23 !!!parse-error (type => 'bogus comment');
1575 wakaba 1.57 $self->{state} = DATA_STATE;
1576 wakaba 1.23 !!!next-input-character;
1577    
1578     !!!emit ($self->{current_token}); # comment
1579    
1580     redo A;
1581 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1582 wakaba 1.77 !!!cp (143);
1583 wakaba 1.23 !!!parse-error (type => 'unclosed comment');
1584 wakaba 1.57 $self->{state} = DATA_STATE;
1585 wakaba 1.23 ## reconsume
1586    
1587     !!!emit ($self->{current_token}); # comment
1588    
1589     redo A;
1590     } else {
1591 wakaba 1.77 !!!cp (144);
1592 wakaba 1.23 $self->{current_token}->{data} # comment
1593 wakaba 1.76 .= '-' . chr ($self->{next_char});
1594 wakaba 1.57 $self->{state} = COMMENT_STATE;
1595 wakaba 1.23 !!!next-input-character;
1596     redo A;
1597     }
1598 wakaba 1.57 } elsif ($self->{state} == COMMENT_STATE) {
1599 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
1600 wakaba 1.77 !!!cp (145);
1601 wakaba 1.57 $self->{state} = COMMENT_END_DASH_STATE;
1602 wakaba 1.1 !!!next-input-character;
1603     redo A;
1604 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1605 wakaba 1.77 !!!cp (146);
1606 wakaba 1.3 !!!parse-error (type => 'unclosed comment');
1607 wakaba 1.57 $self->{state} = DATA_STATE;
1608 wakaba 1.1 ## reconsume
1609    
1610     !!!emit ($self->{current_token}); # comment
1611    
1612     redo A;
1613     } else {
1614 wakaba 1.77 !!!cp (147);
1615 wakaba 1.76 $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
1616 wakaba 1.1 ## Stay in the state
1617     !!!next-input-character;
1618     redo A;
1619     }
1620 wakaba 1.57 } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
1621 wakaba 1.76 if ($self->{next_char} == 0x002D) { # -
1622 wakaba 1.77 !!!cp (148);
1623 wakaba 1.57 $self->{state} = COMMENT_END_STATE;
1624 wakaba 1.1 !!!next-input-character;
1625     redo A;
1626 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1627 wakaba 1.77 !!!cp (149);
1628 wakaba 1.3 !!!parse-error (type => 'unclosed comment');
1629 wakaba 1.57 $self->{state} = DATA_STATE;
1630 wakaba 1.1 ## reconsume
1631    
1632     !!!emit ($self->{current_token}); # comment
1633    
1634     redo A;
1635     } else {
1636 wakaba 1.77 !!!cp (150);
1637 wakaba 1.76 $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment
1638 wakaba 1.57 $self->{state} = COMMENT_STATE;
1639 wakaba 1.1 !!!next-input-character;
1640     redo A;
1641     }
1642 wakaba 1.57 } elsif ($self->{state} == COMMENT_END_STATE) {
1643 wakaba 1.76 if ($self->{next_char} == 0x003E) { # >
1644 wakaba 1.77 !!!cp (151);
1645 wakaba 1.57 $self->{state} = DATA_STATE;
1646 wakaba 1.1 !!!next-input-character;
1647    
1648     !!!emit ($self->{current_token}); # comment
1649    
1650     redo A;
1651 wakaba 1.76 } elsif ($self->{next_char} == 0x002D) { # -
1652 wakaba 1.77 !!!cp (152);
1653 wakaba 1.114 !!!parse-error (type => 'dash in comment',
1654     line => $self->{line_prev},
1655     column => $self->{column_prev});
1656 wakaba 1.1 $self->{current_token}->{data} .= '-'; # comment
1657     ## Stay in the state
1658     !!!next-input-character;
1659     redo A;
1660 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1661 wakaba 1.77 !!!cp (153);
1662 wakaba 1.3 !!!parse-error (type => 'unclosed comment');
1663 wakaba 1.57 $self->{state} = DATA_STATE;
1664 wakaba 1.1 ## reconsume
1665    
1666     !!!emit ($self->{current_token}); # comment
1667    
1668     redo A;
1669     } else {
1670 wakaba 1.77 !!!cp (154);
1671 wakaba 1.114 !!!parse-error (type => 'dash in comment',
1672     line => $self->{line_prev},
1673     column => $self->{column_prev});
1674 wakaba 1.76 $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment
1675 wakaba 1.57 $self->{state} = COMMENT_STATE;
1676 wakaba 1.1 !!!next-input-character;
1677     redo A;
1678     }
1679 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_STATE) {
1680 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1681     $self->{next_char} == 0x000A or # LF
1682     $self->{next_char} == 0x000B or # VT
1683     $self->{next_char} == 0x000C or # FF
1684     $self->{next_char} == 0x0020) { # SP
1685 wakaba 1.77 !!!cp (155);
1686 wakaba 1.57 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1687 wakaba 1.1 !!!next-input-character;
1688     redo A;
1689     } else {
1690 wakaba 1.77 !!!cp (156);
1691 wakaba 1.3 !!!parse-error (type => 'no space before DOCTYPE name');
1692 wakaba 1.57 $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1693 wakaba 1.1 ## reconsume
1694     redo A;
1695     }
1696 wakaba 1.57 } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
1697 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1698     $self->{next_char} == 0x000A or # LF
1699     $self->{next_char} == 0x000B or # VT
1700     $self->{next_char} == 0x000C or # FF
1701     $self->{next_char} == 0x0020) { # SP
1702 wakaba 1.77 !!!cp (157);
1703 wakaba 1.1 ## Stay in the state
1704     !!!next-input-character;
1705     redo A;
1706 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1707 wakaba 1.77 !!!cp (158);
1708 wakaba 1.3 !!!parse-error (type => 'no DOCTYPE name');
1709 wakaba 1.57 $self->{state} = DATA_STATE;
1710 wakaba 1.1 !!!next-input-character;
1711    
1712 wakaba 1.112 !!!emit ($self->{current_token}); # DOCTYPE (quirks)
1713 wakaba 1.1
1714     redo A;
1715 wakaba 1.77 } elsif ($self->{next_char} == -1) {
1716     !!!cp (159);
1717 wakaba 1.3 !!!parse-error (type => 'no DOCTYPE name');
1718 wakaba 1.57 $self->{state} = DATA_STATE;
1719 wakaba 1.1 ## reconsume
1720    
1721 wakaba 1.112 !!!emit ($self->{current_token}); # DOCTYPE (quirks)
1722 wakaba 1.1
1723     redo A;
1724     } else {
1725 wakaba 1.77 !!!cp (160);
1726 wakaba 1.112 $self->{current_token}->{name} = chr $self->{next_char};
1727     delete $self->{current_token}->{quirks};
1728 wakaba 1.4 ## ISSUE: "Set the token's name name to the" in the spec
1729 wakaba 1.57 $self->{state} = DOCTYPE_NAME_STATE;
1730 wakaba 1.1 !!!next-input-character;
1731     redo A;
1732     }
1733 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
1734 wakaba 1.18 ## ISSUE: Redundant "First," in the spec.
1735 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1736     $self->{next_char} == 0x000A or # LF
1737     $self->{next_char} == 0x000B or # VT
1738     $self->{next_char} == 0x000C or # FF
1739     $self->{next_char} == 0x0020) { # SP
1740 wakaba 1.77 !!!cp (161);
1741 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_NAME_STATE;
1742 wakaba 1.1 !!!next-input-character;
1743     redo A;
1744 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1745 wakaba 1.77 !!!cp (162);
1746 wakaba 1.57 $self->{state} = DATA_STATE;
1747 wakaba 1.1 !!!next-input-character;
1748    
1749     !!!emit ($self->{current_token}); # DOCTYPE
1750    
1751     redo A;
1752 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1753 wakaba 1.77 !!!cp (163);
1754 wakaba 1.3 !!!parse-error (type => 'unclosed DOCTYPE');
1755 wakaba 1.57 $self->{state} = DATA_STATE;
1756 wakaba 1.1 ## reconsume
1757    
1758 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1759 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
1760 wakaba 1.1
1761     redo A;
1762     } else {
1763 wakaba 1.77 !!!cp (164);
1764 wakaba 1.1 $self->{current_token}->{name}
1765 wakaba 1.76 .= chr ($self->{next_char}); # DOCTYPE
1766 wakaba 1.1 ## Stay in the state
1767     !!!next-input-character;
1768     redo A;
1769     }
1770 wakaba 1.57 } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
1771 wakaba 1.76 if ($self->{next_char} == 0x0009 or # HT
1772     $self->{next_char} == 0x000A or # LF
1773     $self->{next_char} == 0x000B or # VT
1774     $self->{next_char} == 0x000C or # FF
1775     $self->{next_char} == 0x0020) { # SP
1776 wakaba 1.77 !!!cp (165);
1777 wakaba 1.1 ## Stay in the state
1778     !!!next-input-character;
1779     redo A;
1780 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1781 wakaba 1.77 !!!cp (166);
1782 wakaba 1.57 $self->{state} = DATA_STATE;
1783 wakaba 1.1 !!!next-input-character;
1784    
1785     !!!emit ($self->{current_token}); # DOCTYPE
1786    
1787     redo A;
1788 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1789 wakaba 1.77 !!!cp (167);
1790 wakaba 1.3 !!!parse-error (type => 'unclosed DOCTYPE');
1791 wakaba 1.57 $self->{state} = DATA_STATE;
1792 wakaba 1.1 ## reconsume
1793    
1794 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1795 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
1796    
1797     redo A;
1798 wakaba 1.76 } elsif ($self->{next_char} == 0x0050 or # P
1799     $self->{next_char} == 0x0070) { # p
1800 wakaba 1.18 !!!next-input-character;
1801 wakaba 1.76 if ($self->{next_char} == 0x0055 or # U
1802     $self->{next_char} == 0x0075) { # u
1803 wakaba 1.18 !!!next-input-character;
1804 wakaba 1.76 if ($self->{next_char} == 0x0042 or # B
1805     $self->{next_char} == 0x0062) { # b
1806 wakaba 1.18 !!!next-input-character;
1807 wakaba 1.76 if ($self->{next_char} == 0x004C or # L
1808     $self->{next_char} == 0x006C) { # l
1809 wakaba 1.18 !!!next-input-character;
1810 wakaba 1.76 if ($self->{next_char} == 0x0049 or # I
1811     $self->{next_char} == 0x0069) { # i
1812 wakaba 1.18 !!!next-input-character;
1813 wakaba 1.76 if ($self->{next_char} == 0x0043 or # C
1814     $self->{next_char} == 0x0063) { # c
1815 wakaba 1.77 !!!cp (168);
1816 wakaba 1.57 $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1817 wakaba 1.18 !!!next-input-character;
1818     redo A;
1819 wakaba 1.77 } else {
1820     !!!cp (169);
1821 wakaba 1.18 }
1822 wakaba 1.77 } else {
1823     !!!cp (170);
1824 wakaba 1.18 }
1825 wakaba 1.77 } else {
1826     !!!cp (171);
1827 wakaba 1.18 }
1828 wakaba 1.77 } else {
1829     !!!cp (172);
1830 wakaba 1.18 }
1831 wakaba 1.77 } else {
1832     !!!cp (173);
1833 wakaba 1.18 }
1834    
1835     #
1836 wakaba 1.76 } elsif ($self->{next_char} == 0x0053 or # S
1837     $self->{next_char} == 0x0073) { # s
1838 wakaba 1.18 !!!next-input-character;
1839 wakaba 1.76 if ($self->{next_char} == 0x0059 or # Y
1840     $self->{next_char} == 0x0079) { # y
1841 wakaba 1.18 !!!next-input-character;
1842 wakaba 1.76 if ($self->{next_char} == 0x0053 or # S
1843     $self->{next_char} == 0x0073) { # s
1844 wakaba 1.18 !!!next-input-character;
1845 wakaba 1.76 if ($self->{next_char} == 0x0054 or # T
1846     $self->{next_char} == 0x0074) { # t
1847 wakaba 1.18 !!!next-input-character;
1848 wakaba 1.76 if ($self->{next_char} == 0x0045 or # E
1849     $self->{next_char} == 0x0065) { # e
1850 wakaba 1.18 !!!next-input-character;
1851 wakaba 1.76 if ($self->{next_char} == 0x004D or # M
1852     $self->{next_char} == 0x006D) { # m
1853 wakaba 1.77 !!!cp (174);
1854 wakaba 1.57 $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
1855 wakaba 1.18 !!!next-input-character;
1856     redo A;
1857 wakaba 1.77 } else {
1858     !!!cp (175);
1859 wakaba 1.18 }
1860 wakaba 1.77 } else {
1861     !!!cp (176);
1862 wakaba 1.18 }
1863 wakaba 1.77 } else {
1864     !!!cp (177);
1865 wakaba 1.18 }
1866 wakaba 1.77 } else {
1867     !!!cp (178);
1868 wakaba 1.18 }
1869 wakaba 1.77 } else {
1870     !!!cp (179);
1871 wakaba 1.18 }
1872    
1873     #
1874     } else {
1875 wakaba 1.77 !!!cp (180);
1876 wakaba 1.18 !!!next-input-character;
1877     #
1878     }
1879    
1880     !!!parse-error (type => 'string after DOCTYPE name');
1881 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1882 wakaba 1.73
1883 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
1884 wakaba 1.18 # next-input-character is already done
1885     redo A;
1886 wakaba 1.57 } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
1887 wakaba 1.18 if ({
1888     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1889     #0x000D => 1, # HT, LF, VT, FF, SP, CR
1890 wakaba 1.76 }->{$self->{next_char}}) {
1891 wakaba 1.77 !!!cp (181);
1892 wakaba 1.18 ## Stay in the state
1893     !!!next-input-character;
1894     redo A;
1895 wakaba 1.76 } elsif ($self->{next_char} eq 0x0022) { # "
1896 wakaba 1.77 !!!cp (182);
1897 wakaba 1.18 $self->{current_token}->{public_identifier} = ''; # DOCTYPE
1898 wakaba 1.57 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
1899 wakaba 1.18 !!!next-input-character;
1900     redo A;
1901 wakaba 1.76 } elsif ($self->{next_char} eq 0x0027) { # '
1902 wakaba 1.77 !!!cp (183);
1903 wakaba 1.18 $self->{current_token}->{public_identifier} = ''; # DOCTYPE
1904 wakaba 1.57 $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
1905 wakaba 1.18 !!!next-input-character;
1906     redo A;
1907 wakaba 1.76 } elsif ($self->{next_char} eq 0x003E) { # >
1908 wakaba 1.77 !!!cp (184);
1909 wakaba 1.18 !!!parse-error (type => 'no PUBLIC literal');
1910    
1911 wakaba 1.57 $self->{state} = DATA_STATE;
1912 wakaba 1.18 !!!next-input-character;
1913    
1914 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1915 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
1916    
1917     redo A;
1918 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1919 wakaba 1.77 !!!cp (185);
1920 wakaba 1.18 !!!parse-error (type => 'unclosed DOCTYPE');
1921    
1922 wakaba 1.57 $self->{state} = DATA_STATE;
1923 wakaba 1.18 ## reconsume
1924    
1925 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1926 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
1927    
1928     redo A;
1929     } else {
1930 wakaba 1.77 !!!cp (186);
1931 wakaba 1.18 !!!parse-error (type => 'string after PUBLIC');
1932 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1933 wakaba 1.73
1934 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
1935 wakaba 1.18 !!!next-input-character;
1936     redo A;
1937     }
1938 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
1939 wakaba 1.76 if ($self->{next_char} == 0x0022) { # "
1940 wakaba 1.77 !!!cp (187);
1941 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1942 wakaba 1.18 !!!next-input-character;
1943     redo A;
1944 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1945 wakaba 1.77 !!!cp (188);
1946 wakaba 1.69 !!!parse-error (type => 'unclosed PUBLIC literal');
1947    
1948     $self->{state} = DATA_STATE;
1949     !!!next-input-character;
1950    
1951 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1952 wakaba 1.69 !!!emit ($self->{current_token}); # DOCTYPE
1953    
1954     redo A;
1955 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1956 wakaba 1.77 !!!cp (189);
1957 wakaba 1.18 !!!parse-error (type => 'unclosed PUBLIC literal');
1958    
1959 wakaba 1.57 $self->{state} = DATA_STATE;
1960 wakaba 1.18 ## reconsume
1961    
1962 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1963 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
1964    
1965     redo A;
1966     } else {
1967 wakaba 1.77 !!!cp (190);
1968 wakaba 1.18 $self->{current_token}->{public_identifier} # DOCTYPE
1969 wakaba 1.76 .= chr $self->{next_char};
1970 wakaba 1.18 ## Stay in the state
1971     !!!next-input-character;
1972     redo A;
1973     }
1974 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
1975 wakaba 1.76 if ($self->{next_char} == 0x0027) { # '
1976 wakaba 1.77 !!!cp (191);
1977 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1978 wakaba 1.18 !!!next-input-character;
1979     redo A;
1980 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
1981 wakaba 1.77 !!!cp (192);
1982 wakaba 1.69 !!!parse-error (type => 'unclosed PUBLIC literal');
1983    
1984     $self->{state} = DATA_STATE;
1985     !!!next-input-character;
1986    
1987 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1988 wakaba 1.69 !!!emit ($self->{current_token}); # DOCTYPE
1989    
1990     redo A;
1991 wakaba 1.76 } elsif ($self->{next_char} == -1) {
1992 wakaba 1.77 !!!cp (193);
1993 wakaba 1.18 !!!parse-error (type => 'unclosed PUBLIC literal');
1994    
1995 wakaba 1.57 $self->{state} = DATA_STATE;
1996 wakaba 1.18 ## reconsume
1997    
1998 wakaba 1.75 $self->{current_token}->{quirks} = 1;
1999 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
2000    
2001     redo A;
2002     } else {
2003 wakaba 1.77 !!!cp (194);
2004 wakaba 1.18 $self->{current_token}->{public_identifier} # DOCTYPE
2005 wakaba 1.76 .= chr $self->{next_char};
2006 wakaba 1.18 ## Stay in the state
2007     !!!next-input-character;
2008     redo A;
2009     }
2010 wakaba 1.57 } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2011 wakaba 1.18 if ({
2012     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2013     #0x000D => 1, # HT, LF, VT, FF, SP, CR
2014 wakaba 1.76 }->{$self->{next_char}}) {
2015 wakaba 1.77 !!!cp (195);
2016 wakaba 1.18 ## Stay in the state
2017     !!!next-input-character;
2018     redo A;
2019 wakaba 1.76 } elsif ($self->{next_char} == 0x0022) { # "
2020 wakaba 1.77 !!!cp (196);
2021 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2022 wakaba 1.57 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2023 wakaba 1.18 !!!next-input-character;
2024     redo A;
2025 wakaba 1.76 } elsif ($self->{next_char} == 0x0027) { # '
2026 wakaba 1.77 !!!cp (197);
2027 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2028 wakaba 1.57 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2029 wakaba 1.18 !!!next-input-character;
2030     redo A;
2031 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2032 wakaba 1.77 !!!cp (198);
2033 wakaba 1.57 $self->{state} = DATA_STATE;
2034 wakaba 1.18 !!!next-input-character;
2035    
2036     !!!emit ($self->{current_token}); # DOCTYPE
2037    
2038     redo A;
2039 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2040 wakaba 1.77 !!!cp (199);
2041 wakaba 1.18 !!!parse-error (type => 'unclosed DOCTYPE');
2042    
2043 wakaba 1.57 $self->{state} = DATA_STATE;
2044 wakaba 1.26 ## reconsume
2045 wakaba 1.18
2046 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2047 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
2048    
2049     redo A;
2050     } else {
2051 wakaba 1.77 !!!cp (200);
2052 wakaba 1.18 !!!parse-error (type => 'string after PUBLIC literal');
2053 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2054 wakaba 1.73
2055 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
2056 wakaba 1.18 !!!next-input-character;
2057     redo A;
2058     }
2059 wakaba 1.57 } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2060 wakaba 1.18 if ({
2061     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2062     #0x000D => 1, # HT, LF, VT, FF, SP, CR
2063 wakaba 1.76 }->{$self->{next_char}}) {
2064 wakaba 1.77 !!!cp (201);
2065 wakaba 1.18 ## Stay in the state
2066     !!!next-input-character;
2067     redo A;
2068 wakaba 1.76 } elsif ($self->{next_char} == 0x0022) { # "
2069 wakaba 1.77 !!!cp (202);
2070 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2071 wakaba 1.57 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2072 wakaba 1.18 !!!next-input-character;
2073     redo A;
2074 wakaba 1.76 } elsif ($self->{next_char} == 0x0027) { # '
2075 wakaba 1.77 !!!cp (203);
2076 wakaba 1.18 $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2077 wakaba 1.57 $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2078 wakaba 1.18 !!!next-input-character;
2079     redo A;
2080 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2081 wakaba 1.77 !!!cp (204);
2082 wakaba 1.18 !!!parse-error (type => 'no SYSTEM literal');
2083 wakaba 1.57 $self->{state} = DATA_STATE;
2084 wakaba 1.18 !!!next-input-character;
2085    
2086 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2087 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
2088    
2089     redo A;
2090 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2091 wakaba 1.77 !!!cp (205);
2092 wakaba 1.18 !!!parse-error (type => 'unclosed DOCTYPE');
2093    
2094 wakaba 1.57 $self->{state} = DATA_STATE;
2095 wakaba 1.26 ## reconsume
2096 wakaba 1.18
2097 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2098 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
2099    
2100     redo A;
2101     } else {
2102 wakaba 1.77 !!!cp (206);
2103 wakaba 1.30 !!!parse-error (type => 'string after SYSTEM');
2104 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2105 wakaba 1.73
2106 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
2107 wakaba 1.18 !!!next-input-character;
2108     redo A;
2109     }
2110 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2111 wakaba 1.76 if ($self->{next_char} == 0x0022) { # "
2112 wakaba 1.77 !!!cp (207);
2113 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2114 wakaba 1.18 !!!next-input-character;
2115     redo A;
2116 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2117 wakaba 1.77 !!!cp (208);
2118 wakaba 1.69 !!!parse-error (type => 'unclosed PUBLIC literal');
2119    
2120     $self->{state} = DATA_STATE;
2121     !!!next-input-character;
2122    
2123 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2124 wakaba 1.69 !!!emit ($self->{current_token}); # DOCTYPE
2125    
2126     redo A;
2127 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2128 wakaba 1.77 !!!cp (209);
2129 wakaba 1.18 !!!parse-error (type => 'unclosed SYSTEM literal');
2130    
2131 wakaba 1.57 $self->{state} = DATA_STATE;
2132 wakaba 1.18 ## reconsume
2133    
2134 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2135 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
2136    
2137     redo A;
2138     } else {
2139 wakaba 1.77 !!!cp (210);
2140 wakaba 1.18 $self->{current_token}->{system_identifier} # DOCTYPE
2141 wakaba 1.76 .= chr $self->{next_char};
2142 wakaba 1.18 ## Stay in the state
2143     !!!next-input-character;
2144     redo A;
2145     }
2146 wakaba 1.57 } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2147 wakaba 1.76 if ($self->{next_char} == 0x0027) { # '
2148 wakaba 1.77 !!!cp (211);
2149 wakaba 1.57 $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2150 wakaba 1.18 !!!next-input-character;
2151     redo A;
2152 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2153 wakaba 1.77 !!!cp (212);
2154 wakaba 1.69 !!!parse-error (type => 'unclosed PUBLIC literal');
2155    
2156     $self->{state} = DATA_STATE;
2157     !!!next-input-character;
2158    
2159 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2160 wakaba 1.69 !!!emit ($self->{current_token}); # DOCTYPE
2161    
2162     redo A;
2163 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2164 wakaba 1.77 !!!cp (213);
2165 wakaba 1.18 !!!parse-error (type => 'unclosed SYSTEM literal');
2166    
2167 wakaba 1.57 $self->{state} = DATA_STATE;
2168 wakaba 1.18 ## reconsume
2169    
2170 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2171 wakaba 1.1 !!!emit ($self->{current_token}); # DOCTYPE
2172    
2173     redo A;
2174     } else {
2175 wakaba 1.77 !!!cp (214);
2176 wakaba 1.18 $self->{current_token}->{system_identifier} # DOCTYPE
2177 wakaba 1.76 .= chr $self->{next_char};
2178 wakaba 1.18 ## Stay in the state
2179     !!!next-input-character;
2180     redo A;
2181     }
2182 wakaba 1.57 } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2183 wakaba 1.18 if ({
2184     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2185     #0x000D => 1, # HT, LF, VT, FF, SP, CR
2186 wakaba 1.76 }->{$self->{next_char}}) {
2187 wakaba 1.77 !!!cp (215);
2188 wakaba 1.18 ## Stay in the state
2189     !!!next-input-character;
2190     redo A;
2191 wakaba 1.76 } elsif ($self->{next_char} == 0x003E) { # >
2192 wakaba 1.77 !!!cp (216);
2193 wakaba 1.57 $self->{state} = DATA_STATE;
2194 wakaba 1.18 !!!next-input-character;
2195    
2196     !!!emit ($self->{current_token}); # DOCTYPE
2197    
2198     redo A;
2199 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2200 wakaba 1.77 !!!cp (217);
2201 wakaba 1.18 !!!parse-error (type => 'unclosed DOCTYPE');
2202    
2203 wakaba 1.57 $self->{state} = DATA_STATE;
2204 wakaba 1.26 ## reconsume
2205 wakaba 1.18
2206 wakaba 1.75 $self->{current_token}->{quirks} = 1;
2207 wakaba 1.18 !!!emit ($self->{current_token}); # DOCTYPE
2208    
2209     redo A;
2210     } else {
2211 wakaba 1.77 !!!cp (218);
2212 wakaba 1.18 !!!parse-error (type => 'string after SYSTEM literal');
2213 wakaba 1.75 #$self->{current_token}->{quirks} = 1;
2214 wakaba 1.73
2215 wakaba 1.57 $self->{state} = BOGUS_DOCTYPE_STATE;
2216 wakaba 1.1 !!!next-input-character;
2217     redo A;
2218     }
2219 wakaba 1.57 } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2220 wakaba 1.76 if ($self->{next_char} == 0x003E) { # >
2221 wakaba 1.77 !!!cp (219);
2222 wakaba 1.57 $self->{state} = DATA_STATE;
2223 wakaba 1.1 !!!next-input-character;
2224    
2225     !!!emit ($self->{current_token}); # DOCTYPE
2226    
2227     redo A;
2228 wakaba 1.76 } elsif ($self->{next_char} == -1) {
2229 wakaba 1.77 !!!cp (220);
2230 wakaba 1.3 !!!parse-error (type => 'unclosed DOCTYPE');
2231 wakaba 1.57 $self->{state} = DATA_STATE;
2232 wakaba 1.1 ## reconsume
2233    
2234     !!!emit ($self->{current_token}); # DOCTYPE
2235    
2236     redo A;
2237     } else {
2238 wakaba 1.77 !!!cp (221);
2239 wakaba 1.1 ## Stay in the state
2240     !!!next-input-character;
2241     redo A;
2242     }
2243     } else {
2244     die "$0: $self->{state}: Unknown state";
2245     }
2246     } # A
2247    
2248     die "$0: _get_next_token: unexpected case";
2249     } # _get_next_token
2250    
2251 wakaba 1.72 sub _tokenize_attempt_to_consume_an_entity ($$$) {
2252     my ($self, $in_attr, $additional) = @_;
2253 wakaba 1.20
2254 wakaba 1.112 my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
2255    
2256 wakaba 1.20 if ({
2257     0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
2258     0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR
2259 wakaba 1.72 $additional => 1,
2260 wakaba 1.76 }->{$self->{next_char}}) {
2261 wakaba 1.78 !!!cp (1001);
2262 wakaba 1.20 ## Don't consume
2263     ## No error
2264     return undef;
2265 wakaba 1.76 } elsif ($self->{next_char} == 0x0023) { # #
2266 wakaba 1.1 !!!next-input-character;
2267 wakaba 1.76 if ($self->{next_char} == 0x0078 or # x
2268     $self->{next_char} == 0x0058) { # X
2269 wakaba 1.26 my $code;
2270 wakaba 1.1 X: {
2271 wakaba 1.76 my $x_char = $self->{next_char};
2272 wakaba 1.1 !!!next-input-character;
2273 wakaba 1.76 if (0x0030 <= $self->{next_char} and
2274     $self->{next_char} <= 0x0039) { # 0..9
2275 wakaba 1.78 !!!cp (1002);
2276 wakaba 1.26 $code ||= 0;
2277     $code *= 0x10;
2278 wakaba 1.76 $code += $self->{next_char} - 0x0030;
2279 wakaba 1.1 redo X;
2280 wakaba 1.76 } elsif (0x0061 <= $self->{next_char} and
2281     $self->{next_char} <= 0x0066) { # a..f
2282 wakaba 1.78 !!!cp (1003);
2283 wakaba 1.26 $code ||= 0;
2284     $code *= 0x10;
2285 wakaba 1.76 $code += $self->{next_char} - 0x0060 + 9;
2286 wakaba 1.1 redo X;
2287 wakaba 1.76 } elsif (0x0041 <= $self->{next_char} and
2288     $self->{next_char} <= 0x0046) { # A..F
2289 wakaba 1.78 !!!cp (1004);
2290 wakaba 1.26 $code ||= 0;
2291     $code *= 0x10;
2292 wakaba 1.76 $code += $self->{next_char} - 0x0040 + 9;
2293 wakaba 1.1 redo X;
2294 wakaba 1.26 } elsif (not defined $code) { # no hexadecimal digit
2295 wakaba 1.78 !!!cp (1005);
2296 wakaba 1.112 !!!parse-error (type => 'bare hcro', line => $l, column => $c);
2297 wakaba 1.76 !!!back-next-input-character ($x_char, $self->{next_char});
2298     $self->{next_char} = 0x0023; # #
2299 wakaba 1.1 return undef;
2300 wakaba 1.76 } elsif ($self->{next_char} == 0x003B) { # ;
2301 wakaba 1.78 !!!cp (1006);
2302 wakaba 1.1 !!!next-input-character;
2303     } else {
2304 wakaba 1.78 !!!cp (1007);
2305 wakaba 1.112 !!!parse-error (type => 'no refc', line => $l, column => $c);
2306 wakaba 1.1 }
2307    
2308 wakaba 1.26 if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
2309 wakaba 1.78 !!!cp (1008);
2310 wakaba 1.112 !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
2311 wakaba 1.26 $code = 0xFFFD;
2312     } elsif ($code > 0x10FFFF) {
2313 wakaba 1.78 !!!cp (1009);
2314 wakaba 1.112 !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
2315 wakaba 1.26 $code = 0xFFFD;
2316     } elsif ($code == 0x000D) {
2317 wakaba 1.78 !!!cp (1010);
2318 wakaba 1.112 !!!parse-error (type => 'CR character reference', line => $l, column => $c);
2319 wakaba 1.26 $code = 0x000A;
2320     } elsif (0x80 <= $code and $code <= 0x9F) {
2321 wakaba 1.78 !!!cp (1011);
2322 wakaba 1.112 !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
2323 wakaba 1.26 $code = $c1_entity_char->{$code};
2324 wakaba 1.1 }
2325    
2326 wakaba 1.66 return {type => CHARACTER_TOKEN, data => chr $code,
2327 wakaba 1.118 has_reference => 1,
2328 wakaba 1.120 line => $l, column => $c,
2329 wakaba 1.118 };
2330 wakaba 1.1 } # X
2331 wakaba 1.76 } elsif (0x0030 <= $self->{next_char} and
2332     $self->{next_char} <= 0x0039) { # 0..9
2333     my $code = $self->{next_char} - 0x0030;
2334 wakaba 1.1 !!!next-input-character;
2335    
2336 wakaba 1.76 while (0x0030 <= $self->{next_char} and
2337     $self->{next_char} <= 0x0039) { # 0..9
2338 wakaba 1.78 !!!cp (1012);
2339 wakaba 1.1 $code *= 10;
2340 wakaba 1.76 $code += $self->{next_char} - 0x0030;
2341 wakaba 1.1
2342     !!!next-input-character;
2343     }
2344    
2345 wakaba 1.76 if ($self->{next_char} == 0x003B) { # ;
2346 wakaba 1.78 !!!cp (1013);
2347 wakaba 1.1 !!!next-input-character;
2348     } else {
2349 wakaba 1.78 !!!cp (1014);
2350 wakaba 1.112 !!!parse-error (type => 'no refc', line => $l, column => $c);
2351 wakaba 1.1 }
2352    
2353 wakaba 1.26 if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
2354 wakaba 1.78 !!!cp (1015);
2355 wakaba 1.112 !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
2356 wakaba 1.26 $code = 0xFFFD;
2357     } elsif ($code > 0x10FFFF) {
2358 wakaba 1.78 !!!cp (1016);
2359 wakaba 1.112 !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
2360 wakaba 1.26 $code = 0xFFFD;
2361     } elsif ($code == 0x000D) {
2362 wakaba 1.78 !!!cp (1017);
2363 wakaba 1.112 !!!parse-error (type => 'CR character reference', line => $l, column => $c);
2364 wakaba 1.26 $code = 0x000A;
2365 wakaba 1.4 } elsif (0x80 <= $code and $code <= 0x9F) {
2366 wakaba 1.78 !!!cp (1018);
2367 wakaba 1.112 !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
2368 wakaba 1.4 $code = $c1_entity_char->{$code};
2369 wakaba 1.1 }
2370    
2371 wakaba 1.112 return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,
2372 wakaba 1.120 line => $l, column => $c,
2373 wakaba 1.118 };
2374 wakaba 1.1 } else {
2375 wakaba 1.78 !!!cp (1019);
2376 wakaba 1.112 !!!parse-error (type => 'bare nero', line => $l, column => $c);
2377 wakaba 1.76 !!!back-next-input-character ($self->{next_char});
2378     $self->{next_char} = 0x0023; # #
2379 wakaba 1.1 return undef;
2380     }
2381 wakaba 1.76 } elsif ((0x0041 <= $self->{next_char} and
2382     $self->{next_char} <= 0x005A) or
2383     (0x0061 <= $self->{next_char} and
2384     $self->{next_char} <= 0x007A)) {
2385     my $entity_name = chr $self->{next_char};
2386 wakaba 1.1 !!!next-input-character;
2387    
2388     my $value = $entity_name;
2389 wakaba 1.37 my $match = 0;
2390 wakaba 1.16 require Whatpm::_NamedEntityList;
2391     our $EntityChar;
2392 wakaba 1.1
2393     while (length $entity_name < 10 and
2394     ## NOTE: Some number greater than the maximum length of entity name
2395 wakaba 1.76 ((0x0041 <= $self->{next_char} and # a
2396     $self->{next_char} <= 0x005A) or # x
2397     (0x0061 <= $self->{next_char} and # a
2398     $self->{next_char} <= 0x007A) or # z
2399     (0x0030 <= $self->{next_char} and # 0
2400     $self->{next_char} <= 0x0039) or # 9
2401     $self->{next_char} == 0x003B)) { # ;
2402     $entity_name .= chr $self->{next_char};
2403 wakaba 1.16 if (defined $EntityChar->{$entity_name}) {
2404 wakaba 1.76 if ($self->{next_char} == 0x003B) { # ;
2405 wakaba 1.78 !!!cp (1020);
2406 wakaba 1.26 $value = $EntityChar->{$entity_name};
2407 wakaba 1.16 $match = 1;
2408     !!!next-input-character;
2409     last;
2410 wakaba 1.37 } else {
2411 wakaba 1.78 !!!cp (1021);
2412 wakaba 1.26 $value = $EntityChar->{$entity_name};
2413     $match = -1;
2414 wakaba 1.37 !!!next-input-character;
2415 wakaba 1.16 }
2416 wakaba 1.1 } else {
2417 wakaba 1.78 !!!cp (1022);
2418 wakaba 1.76 $value .= chr $self->{next_char};
2419 wakaba 1.37 $match *= 2;
2420     !!!next-input-character;
2421 wakaba 1.1 }
2422     }
2423    
2424 wakaba 1.16 if ($match > 0) {
2425 wakaba 1.78 !!!cp (1023);
2426 wakaba 1.112 return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
2427 wakaba 1.120 line => $l, column => $c,
2428 wakaba 1.118 };
2429 wakaba 1.16 } elsif ($match < 0) {
2430 wakaba 1.112 !!!parse-error (type => 'no refc', line => $l, column => $c);
2431 wakaba 1.37 if ($in_attr and $match < -1) {
2432 wakaba 1.78 !!!cp (1024);
2433 wakaba 1.112 return {type => CHARACTER_TOKEN, data => '&'.$entity_name,
2434 wakaba 1.120 line => $l, column => $c,
2435 wakaba 1.118 };
2436 wakaba 1.37 } else {
2437 wakaba 1.78 !!!cp (1025);
2438 wakaba 1.112 return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
2439 wakaba 1.120 line => $l, column => $c,
2440 wakaba 1.118 };
2441 wakaba 1.37 }
2442 wakaba 1.1 } else {
2443 wakaba 1.78 !!!cp (1026);
2444 wakaba 1.112 !!!parse-error (type => 'bare ero', line => $l, column => $c);
2445 wakaba 1.66 ## NOTE: "No characters are consumed" in the spec.
2446 wakaba 1.112 return {type => CHARACTER_TOKEN, data => '&'.$value,
2447 wakaba 1.120 line => $l, column => $c,
2448 wakaba 1.118 };
2449 wakaba 1.1 }
2450     } else {
2451 wakaba 1.78 !!!cp (1027);
2452 wakaba 1.1 ## no characters are consumed
2453 wakaba 1.112 !!!parse-error (type => 'bare ero', line => $l, column => $c);
2454 wakaba 1.1 return undef;
2455     }
2456     } # _tokenize_attempt_to_consume_an_entity
2457    
2458     sub _initialize_tree_constructor ($) {
2459     my $self = shift;
2460     ## NOTE: $self->{document} MUST be specified before this method is called
2461     $self->{document}->strict_error_checking (0);
2462     ## TODO: Turn mutation events off # MUST
2463     ## TODO: Turn loose Document option (manakai extension) on
2464 wakaba 1.18 $self->{document}->manakai_is_html (1); # MUST
2465 wakaba 1.1 } # _initialize_tree_constructor
2466    
2467     sub _terminate_tree_constructor ($) {
2468     my $self = shift;
2469     $self->{document}->strict_error_checking (1);
2470     ## TODO: Turn mutation events on
2471     } # _terminate_tree_constructor
2472    
2473     ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?
2474    
2475 wakaba 1.3 { # tree construction stage
2476     my $token;
2477    
2478 wakaba 1.1 sub _construct_tree ($) {
2479     my ($self) = @_;
2480    
2481     ## When an interactive UA render the $self->{document} available
2482     ## to the user, or when it begin accepting user input, are
2483     ## not defined.
2484    
2485     ## Append a character: collect it and all subsequent consecutive
2486     ## characters and insert one Text node whose data is concatenation
2487     ## of all those characters. # MUST
2488    
2489     !!!next-token;
2490    
2491 wakaba 1.3 undef $self->{form_element};
2492     undef $self->{head_element};
2493     $self->{open_elements} = [];
2494     undef $self->{inner_html_node};
2495    
2496 wakaba 1.84 ## NOTE: The "initial" insertion mode.
2497 wakaba 1.3 $self->_tree_construction_initial; # MUST
2498 wakaba 1.84
2499     ## NOTE: The "before html" insertion mode.
2500 wakaba 1.3 $self->_tree_construction_root_element;
2501 wakaba 1.84 $self->{insertion_mode} = BEFORE_HEAD_IM;
2502    
2503     ## NOTE: The "before head" insertion mode and so on.
2504 wakaba 1.3 $self->_tree_construction_main;
2505     } # _construct_tree
2506    
2507     sub _tree_construction_initial ($) {
2508     my $self = shift;
2509 wakaba 1.84
2510     ## NOTE: "initial" insertion mode
2511    
2512 wakaba 1.18 INITIAL: {
2513 wakaba 1.55 if ($token->{type} == DOCTYPE_TOKEN) {
2514 wakaba 1.18 ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
2515     ## error, switch to a conformance checking mode for another
2516     ## language.
2517     my $doctype_name = $token->{name};
2518     $doctype_name = '' unless defined $doctype_name;
2519     $doctype_name =~ tr/a-z/A-Z/;
2520     if (not defined $token->{name} or # <!DOCTYPE>
2521     defined $token->{public_identifier} or
2522     defined $token->{system_identifier}) {
2523 wakaba 1.79 !!!cp ('t1');
2524 wakaba 1.113 !!!parse-error (type => 'not HTML5', token => $token);
2525 wakaba 1.18 } elsif ($doctype_name ne 'HTML') {
2526 wakaba 1.79 !!!cp ('t2');
2527 wakaba 1.18 ## ISSUE: ASCII case-insensitive? (in fact it does not matter)
2528 wakaba 1.113 !!!parse-error (type => 'not HTML5', token => $token);
2529 wakaba 1.79 } else {
2530     !!!cp ('t3');
2531 wakaba 1.18 }
2532    
2533     my $doctype = $self->{document}->create_document_type_definition
2534     ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
2535 wakaba 1.122 ## NOTE: Default value for both |public_id| and |system_id| attributes
2536     ## are empty strings, so that we don't set any value in missing cases.
2537 wakaba 1.18 $doctype->public_id ($token->{public_identifier})
2538     if defined $token->{public_identifier};
2539     $doctype->system_id ($token->{system_identifier})
2540     if defined $token->{system_identifier};
2541     ## NOTE: Other DocumentType attributes are null or empty lists.
2542     ## ISSUE: internalSubset = null??
2543     $self->{document}->append_child ($doctype);
2544    
2545 wakaba 1.75 if ($token->{quirks} or $doctype_name ne 'HTML') {
2546 wakaba 1.79 !!!cp ('t4');
2547 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
2548     } elsif (defined $token->{public_identifier}) {
2549     my $pubid = $token->{public_identifier};
2550     $pubid =~ tr/a-z/A-z/;
2551     if ({
2552     "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,
2553     "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,
2554     "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,
2555     "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,
2556     "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,
2557     "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,
2558     "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,
2559     "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,
2560     "-//IETF//DTD HTML 2.0//EN" => 1,
2561     "-//IETF//DTD HTML 2.1E//EN" => 1,
2562     "-//IETF//DTD HTML 3.0//EN" => 1,
2563     "-//IETF//DTD HTML 3.0//EN//" => 1,
2564     "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,
2565     "-//IETF//DTD HTML 3.2//EN" => 1,
2566     "-//IETF//DTD HTML 3//EN" => 1,
2567     "-//IETF//DTD HTML LEVEL 0//EN" => 1,
2568     "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,
2569     "-//IETF//DTD HTML LEVEL 1//EN" => 1,
2570     "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,
2571     "-//IETF//DTD HTML LEVEL 2//EN" => 1,
2572     "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,
2573     "-//IETF//DTD HTML LEVEL 3//EN" => 1,
2574     "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,
2575     "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,
2576     "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,
2577     "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,
2578     "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,
2579     "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,
2580     "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,
2581     "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,
2582     "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,
2583     "-//IETF//DTD HTML STRICT//EN" => 1,
2584     "-//IETF//DTD HTML STRICT//EN//2.0" => 1,
2585     "-//IETF//DTD HTML STRICT//EN//3.0" => 1,
2586     "-//IETF//DTD HTML//EN" => 1,
2587     "-//IETF//DTD HTML//EN//2.0" => 1,
2588     "-//IETF//DTD HTML//EN//3.0" => 1,
2589     "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,
2590     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,
2591     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,
2592     "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,
2593     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,
2594     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,
2595     "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,
2596     "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,
2597     "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,
2598     "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,
2599     "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,
2600 wakaba 1.72 "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,
2601     "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,
2602     "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,
2603 wakaba 1.18 "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,
2604     "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,
2605     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,
2606     "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,
2607     "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,
2608     "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,
2609     "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,
2610     "-//W3C//DTD HTML 3.2//EN" => 1,
2611     "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,
2612     "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,
2613     "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,
2614     "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,
2615     "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,
2616     "-//W3C//DTD W3 HTML//EN" => 1,
2617     "-//W3O//DTD W3 HTML 3.0//EN" => 1,
2618     "-//W3O//DTD W3 HTML 3.0//EN//" => 1,
2619     "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,
2620     "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,
2621     "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,
2622     "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,
2623     "HTML" => 1,
2624     }->{$pubid}) {
2625 wakaba 1.79 !!!cp ('t5');
2626 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
2627     } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or
2628     $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {
2629     if (defined $token->{system_identifier}) {
2630 wakaba 1.79 !!!cp ('t6');
2631 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
2632     } else {
2633 wakaba 1.79 !!!cp ('t7');
2634 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
2635 wakaba 1.3 }
2636 wakaba 1.80 } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or
2637     $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {
2638 wakaba 1.79 !!!cp ('t8');
2639 wakaba 1.18 $self->{document}->manakai_compat_mode ('limited quirks');
2640 wakaba 1.79 } else {
2641     !!!cp ('t9');
2642 wakaba 1.18 }
2643 wakaba 1.79 } else {
2644     !!!cp ('t10');
2645 wakaba 1.18 }
2646     if (defined $token->{system_identifier}) {
2647     my $sysid = $token->{system_identifier};
2648     $sysid =~ tr/A-Z/a-z/;
2649     if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
2650 wakaba 1.80 ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"
2651 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
2652 wakaba 1.79 !!!cp ('t11');
2653     } else {
2654     !!!cp ('t12');
2655 wakaba 1.18 }
2656 wakaba 1.79 } else {
2657     !!!cp ('t13');
2658 wakaba 1.18 }
2659    
2660 wakaba 1.84 ## Go to the "before html" insertion mode.
2661 wakaba 1.18 !!!next-token;
2662     return;
2663     } elsif ({
2664 wakaba 1.55 START_TAG_TOKEN, 1,
2665     END_TAG_TOKEN, 1,
2666     END_OF_FILE_TOKEN, 1,
2667 wakaba 1.18 }->{$token->{type}}) {
2668 wakaba 1.79 !!!cp ('t14');
2669 wakaba 1.113 !!!parse-error (type => 'no DOCTYPE', token => $token);
2670 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
2671 wakaba 1.84 ## Go to the "before html" insertion mode.
2672 wakaba 1.18 ## reprocess
2673     return;
2674 wakaba 1.55 } elsif ($token->{type} == CHARACTER_TOKEN) {
2675 wakaba 1.18 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
2676     ## Ignore the token
2677 wakaba 1.26
2678 wakaba 1.18 unless (length $token->{data}) {
2679 wakaba 1.79 !!!cp ('t15');
2680 wakaba 1.84 ## Stay in the insertion mode.
2681 wakaba 1.18 !!!next-token;
2682     redo INITIAL;
2683 wakaba 1.79 } else {
2684     !!!cp ('t16');
2685 wakaba 1.3 }
2686 wakaba 1.79 } else {
2687     !!!cp ('t17');
2688 wakaba 1.3 }
2689 wakaba 1.18
2690 wakaba 1.113 !!!parse-error (type => 'no DOCTYPE', token => $token);
2691 wakaba 1.18 $self->{document}->manakai_compat_mode ('quirks');
2692 wakaba 1.84 ## Go to the "before html" insertion mode.
2693 wakaba 1.18 ## reprocess
2694     return;
2695 wakaba 1.55 } elsif ($token->{type} == COMMENT_TOKEN) {
2696 wakaba 1.79 !!!cp ('t18');
2697 wakaba 1.18 my $comment = $self->{document}->create_comment ($token->{data});
2698     $self->{document}->append_child ($comment);
2699    
2700 wakaba 1.84 ## Stay in the insertion mode.
2701 wakaba 1.18 !!!next-token;
2702     redo INITIAL;
2703     } else {
2704 wakaba 1.55 die "$0: $token->{type}: Unknown token type";
2705 wakaba 1.18 }
2706     } # INITIAL
2707 wakaba 1.79
2708     die "$0: _tree_construction_initial: This should be never reached";
2709 wakaba 1.3 } # _tree_construction_initial
2710    
2711     sub _tree_construction_root_element ($) {
2712     my $self = shift;
2713 wakaba 1.84
2714     ## NOTE: "before html" insertion mode.
2715 wakaba 1.3
2716     B: {
2717 wakaba 1.55 if ($token->{type} == DOCTYPE_TOKEN) {
2718 wakaba 1.79 !!!cp ('t19');
2719 wakaba 1.113 !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
2720 wakaba 1.3 ## Ignore the token
2721 wakaba 1.84 ## Stay in the insertion mode.
2722 wakaba 1.3 !!!next-token;
2723     redo B;
2724 wakaba 1.55 } elsif ($token->{type} == COMMENT_TOKEN) {
2725 wakaba 1.79 !!!cp ('t20');
2726 wakaba 1.3 my $comment = $self->{document}->create_comment ($token->{data});
2727     $self->{document}->append_child ($comment);
2728 wakaba 1.84 ## Stay in the insertion mode.
2729 wakaba 1.3 !!!next-token;
2730     redo B;
2731 wakaba 1.55 } elsif ($token->{type} == CHARACTER_TOKEN) {
2732 wakaba 1.26 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
2733     ## Ignore the token.
2734    
2735 wakaba 1.3 unless (length $token->{data}) {
2736 wakaba 1.79 !!!cp ('t21');
2737 wakaba 1.84 ## Stay in the insertion mode.
2738 wakaba 1.3 !!!next-token;
2739     redo B;
2740 wakaba 1.79 } else {
2741     !!!cp ('t22');
2742 wakaba 1.3 }
2743 wakaba 1.79 } else {
2744     !!!cp ('t23');
2745 wakaba 1.3 }
2746 wakaba 1.61
2747     $self->{application_cache_selection}->(undef);
2748    
2749     #
2750     } elsif ($token->{type} == START_TAG_TOKEN) {
2751 wakaba 1.84 if ($token->{tag_name} eq 'html') {
2752     my $root_element;
2753 wakaba 1.116 !!!create-element ($root_element, $token->{tag_name}, $token->{attributes}, $token);
2754 wakaba 1.84 $self->{document}->append_child ($root_element);
2755     push @{$self->{open_elements}}, [$root_element, 'html'];
2756    
2757     if ($token->{attributes}->{manifest}) {
2758     !!!cp ('t24');
2759     $self->{application_cache_selection}
2760     ->($token->{attributes}->{manifest}->{value});
2761 wakaba 1.118 ## ISSUE: Spec is unclear on relative references.
2762     ## According to Hixie (#whatwg 2008-03-19), it should be
2763     ## resolved against the base URI of the document in HTML
2764     ## or xml:base of the element in XHTML.
2765 wakaba 1.84 } else {
2766     !!!cp ('t25');
2767     $self->{application_cache_selection}->(undef);
2768     }
2769    
2770     !!!next-token;
2771     return; ## Go to the "before head" insertion mode.
2772 wakaba 1.61 } else {
2773 wakaba 1.84 !!!cp ('t25.1');
2774     #
2775 wakaba 1.61 }
2776 wakaba 1.3 } elsif ({
2777 wakaba 1.55 END_TAG_TOKEN, 1,
2778     END_OF_FILE_TOKEN, 1,
2779 wakaba 1.3 }->{$token->{type}}) {
2780 wakaba 1.79 !!!cp ('t26');
2781 wakaba 1.3 #
2782     } else {
2783 wakaba 1.55 die "$0: $token->{type}: Unknown token type";
2784 wakaba 1.3 }
2785 wakaba 1.61
2786 wakaba 1.116 my $root_element; !!!create-element ($root_element, 'html',, $token);
2787 wakaba 1.84 $self->{document}->append_child ($root_element);
2788     push @{$self->{open_elements}}, [$root_element, 'html'];
2789    
2790     $self->{application_cache_selection}->(undef);
2791    
2792     ## NOTE: Reprocess the token.
2793     return; ## Go to the "before head" insertion mode.
2794    
2795     ## ISSUE: There is an issue in the spec
2796 wakaba 1.3 } # B
2797 wakaba 1.79
2798     die "$0: _tree_construction_root_element: This should never be reached";
2799 wakaba 1.3 } # _tree_construction_root_element
2800    
2801     sub _reset_insertion_mode ($) {
2802     my $self = shift;
2803    
2804     ## Step 1
2805     my $last;
2806    
2807     ## Step 2
2808     my $i = -1;
2809     my $node = $self->{open_elements}->[$i];
2810    
2811     ## Step 3
2812     S3: {
2813 wakaba 1.29 if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
2814     $last = 1;
2815     if (defined $self->{inner_html_node}) {
2816     if ($self->{inner_html_node}->[1] eq 'td' or
2817     $self->{inner_html_node}->[1] eq 'th') {
2818 wakaba 1.79 !!!cp ('t27');
2819 wakaba 1.29 #
2820     } else {
2821 wakaba 1.79 !!!cp ('t28');
2822 wakaba 1.29 $node = $self->{inner_html_node};
2823     }
2824 wakaba 1.3 }
2825     }
2826    
2827     ## Step 4..13
2828     my $new_mode = {
2829 wakaba 1.54 select => IN_SELECT_IM,
2830 wakaba 1.83 ## NOTE: |option| and |optgroup| do not set
2831     ## insertion mode to "in select" by themselves.
2832 wakaba 1.54 td => IN_CELL_IM,
2833     th => IN_CELL_IM,
2834     tr => IN_ROW_IM,
2835     tbody => IN_TABLE_BODY_IM,
2836     thead => IN_TABLE_BODY_IM,
2837     tfoot => IN_TABLE_BODY_IM,
2838     caption => IN_CAPTION_IM,
2839     colgroup => IN_COLUMN_GROUP_IM,
2840     table => IN_TABLE_IM,
2841     head => IN_BODY_IM, # not in head!
2842     body => IN_BODY_IM,
2843     frameset => IN_FRAMESET_IM,
2844 wakaba 1.3 }->{$node->[1]};
2845     $self->{insertion_mode} = $new_mode and return if defined $new_mode;
2846    
2847     ## Step 14
2848     if ($node->[1] eq 'html') {
2849     unless (defined $self->{head_element}) {
2850 wakaba 1.79 !!!cp ('t29');
2851 wakaba 1.54 $self->{insertion_mode} = BEFORE_HEAD_IM;
2852 wakaba 1.3 } else {
2853 wakaba 1.81 ## ISSUE: Can this state be reached?
2854 wakaba 1.79 !!!cp ('t30');
2855 wakaba 1.54 $self->{insertion_mode} = AFTER_HEAD_IM;
2856 wakaba 1.3 }
2857     return;
2858 wakaba 1.79 } else {
2859     !!!cp ('t31');
2860 wakaba 1.3 }
2861    
2862     ## Step 15
2863 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM and return if $last;
2864 wakaba 1.3
2865     ## Step 16
2866     $i--;
2867     $node = $self->{open_elements}->[$i];
2868    
2869     ## Step 17
2870     redo S3;
2871     } # S3
2872 wakaba 1.79
2873     die "$0: _reset_insertion_mode: This line should never be reached";
2874 wakaba 1.3 } # _reset_insertion_mode
2875    
2876     sub _tree_construction_main ($) {
2877     my $self = shift;
2878    
2879 wakaba 1.1 my $active_formatting_elements = [];
2880    
2881     my $reconstruct_active_formatting_elements = sub { # MUST
2882     my $insert = shift;
2883    
2884     ## Step 1
2885     return unless @$active_formatting_elements;
2886    
2887     ## Step 3
2888     my $i = -1;
2889     my $entry = $active_formatting_elements->[$i];
2890    
2891     ## Step 2
2892     return if $entry->[0] eq '#marker';
2893 wakaba 1.3 for (@{$self->{open_elements}}) {
2894 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
2895 wakaba 1.79 !!!cp ('t32');
2896 wakaba 1.1 return;
2897     }
2898     }
2899    
2900     S4: {
2901     ## Step 4
2902     last S4 if $active_formatting_elements->[0]->[0] eq $entry->[0];
2903    
2904     ## Step 5
2905     $i--;
2906     $entry = $active_formatting_elements->[$i];
2907    
2908     ## Step 6
2909     if ($entry->[0] eq '#marker') {
2910 wakaba 1.81 !!!cp ('t33_1');
2911 wakaba 1.1 #
2912     } else {
2913     my $in_open_elements;
2914 wakaba 1.3 OE: for (@{$self->{open_elements}}) {
2915 wakaba 1.1 if ($entry->[0] eq $_->[0]) {
2916 wakaba 1.79 !!!cp ('t33');
2917 wakaba 1.1 $in_open_elements = 1;
2918     last OE;
2919     }
2920     }
2921     if ($in_open_elements) {
2922 wakaba 1.79 !!!cp ('t34');
2923 wakaba 1.1 #
2924     } else {
2925 wakaba 1.81 ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
2926 wakaba 1.79 !!!cp ('t35');
2927 wakaba 1.1 redo S4;
2928     }
2929     }
2930    
2931     ## Step 7
2932     $i++;
2933     $entry = $active_formatting_elements->[$i];
2934     } # S4
2935    
2936     S7: {
2937     ## Step 8
2938     my $clone = [$entry->[0]->clone_node (0), $entry->[1]];
2939    
2940     ## Step 9
2941     $insert->($clone->[0]);
2942 wakaba 1.3 push @{$self->{open_elements}}, $clone;
2943 wakaba 1.1
2944     ## Step 10
2945 wakaba 1.3 $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
2946 wakaba 1.1
2947     ## Step 11
2948     unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
2949 wakaba 1.79 !!!cp ('t36');
2950 wakaba 1.1 ## Step 7'
2951     $i++;
2952     $entry = $active_formatting_elements->[$i];
2953    
2954     redo S7;
2955     }
2956 wakaba 1.79
2957     !!!cp ('t37');
2958 wakaba 1.1 } # S7
2959     }; # $reconstruct_active_formatting_elements
2960    
2961     my $clear_up_to_marker = sub {
2962     for (reverse 0..$#$active_formatting_elements) {
2963     if ($active_formatting_elements->[$_]->[0] eq '#marker') {
2964 wakaba 1.79 !!!cp ('t38');
2965 wakaba 1.1 splice @$active_formatting_elements, $_;
2966     return;
2967     }
2968     }
2969 wakaba 1.79
2970     !!!cp ('t39');
2971 wakaba 1.1 }; # $clear_up_to_marker
2972    
2973 wakaba 1.96 my $insert;
2974    
2975     my $parse_rcdata = sub ($) {
2976     my ($content_model_flag) = @_;
2977 wakaba 1.25
2978     ## Step 1
2979     my $start_tag_name = $token->{tag_name};
2980     my $el;
2981 wakaba 1.116 !!!create-element ($el, $start_tag_name, $token->{attributes}, $token);
2982 wakaba 1.25
2983     ## Step 2
2984 wakaba 1.96 $insert->($el);
2985 wakaba 1.25
2986     ## Step 3
2987 wakaba 1.40 $self->{content_model} = $content_model_flag; # CDATA or RCDATA
2988 wakaba 1.13 delete $self->{escape}; # MUST
2989 wakaba 1.25
2990     ## Step 4
2991 wakaba 1.1 my $text = '';
2992     !!!next-token;
2993 wakaba 1.55 while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
2994 wakaba 1.79 !!!cp ('t40');
2995 wakaba 1.1 $text .= $token->{data};
2996     !!!next-token;
2997 wakaba 1.25 }
2998    
2999     ## Step 5
3000 wakaba 1.1 if (length $text) {
3001 wakaba 1.79 !!!cp ('t41');
3002 wakaba 1.25 my $text = $self->{document}->create_text_node ($text);
3003     $el->append_child ($text);
3004 wakaba 1.1 }
3005 wakaba 1.25
3006     ## Step 6
3007 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL;
3008 wakaba 1.25
3009     ## Step 7
3010 wakaba 1.79 if ($token->{type} == END_TAG_TOKEN and
3011     $token->{tag_name} eq $start_tag_name) {
3012     !!!cp ('t42');
3013 wakaba 1.1 ## Ignore the token
3014     } else {
3015 wakaba 1.96 ## NOTE: An end-of-file token.
3016     if ($content_model_flag == CDATA_CONTENT_MODEL) {
3017     !!!cp ('t43');
3018 wakaba 1.113 !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);
3019 wakaba 1.96 } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
3020     !!!cp ('t44');
3021 wakaba 1.113 !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);
3022 wakaba 1.96 } else {
3023     die "$0: $content_model_flag in parse_rcdata";
3024     }
3025 wakaba 1.1 }
3026     !!!next-token;
3027 wakaba 1.25 }; # $parse_rcdata
3028 wakaba 1.1
3029 wakaba 1.96 my $script_start_tag = sub () {
3030 wakaba 1.1 my $script_el;
3031 wakaba 1.116 !!!create-element ($script_el, 'script', $token->{attributes}, $token);
3032 wakaba 1.1 ## TODO: mark as "parser-inserted"
3033    
3034 wakaba 1.40 $self->{content_model} = CDATA_CONTENT_MODEL;
3035 wakaba 1.13 delete $self->{escape}; # MUST
3036 wakaba 1.1
3037     my $text = '';
3038     !!!next-token;
3039 wakaba 1.55 while ($token->{type} == CHARACTER_TOKEN) {
3040 wakaba 1.79 !!!cp ('t45');
3041 wakaba 1.1 $text .= $token->{data};
3042     !!!next-token;
3043     } # stop if non-character token or tokenizer stops tokenising
3044     if (length $text) {
3045 wakaba 1.79 !!!cp ('t46');
3046 wakaba 1.1 $script_el->manakai_append_text ($text);
3047     }
3048    
3049 wakaba 1.40 $self->{content_model} = PCDATA_CONTENT_MODEL;
3050 wakaba 1.1
3051 wakaba 1.55 if ($token->{type} == END_TAG_TOKEN and
3052 wakaba 1.1 $token->{tag_name} eq 'script') {
3053 wakaba 1.79 !!!cp ('t47');
3054 wakaba 1.1 ## Ignore the token
3055     } else {
3056 wakaba 1.79 !!!cp ('t48');
3057 wakaba 1.113 !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);
3058 wakaba 1.1 ## ISSUE: And ignore?
3059     ## TODO: mark as "already executed"
3060     }
3061    
3062 wakaba 1.3 if (defined $self->{inner_html_node}) {
3063 wakaba 1.79 !!!cp ('t49');
3064 wakaba 1.3 ## TODO: mark as "already executed"
3065     } else {
3066 wakaba 1.79 !!!cp ('t50');
3067 wakaba 1.1 ## TODO: $old_insertion_point = current insertion point
3068     ## TODO: insertion point = just before the next input character
3069 wakaba 1.25
3070     $insert->($script_el);
3071 wakaba 1.1
3072     ## TODO: insertion point = $old_insertion_point (might be "undefined")
3073    
3074     ## TODO: if there is a script that will execute as soon as the parser resume, then...
3075     }
3076    
3077     !!!next-token;
3078     }; # $script_start_tag
3079    
3080 wakaba 1.102 ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
3081     ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
3082     my $open_tables = [[$self->{open_elements}->[0]->[0]]];
3083    
3084 wakaba 1.1 my $formatting_end_tag = sub {
3085 wakaba 1.113 my $end_tag_token = shift;
3086     my $tag_name = $end_tag_token->{tag_name};
3087 wakaba 1.1
3088 wakaba 1.103 ## NOTE: The adoption agency algorithm (AAA).
3089 wakaba 1.102
3090 wakaba 1.1 FET: {
3091     ## Step 1
3092     my $formatting_element;
3093     my $formatting_element_i_in_active;
3094     AFE: for (reverse 0..$#$active_formatting_elements) {
3095     if ($active_formatting_elements->[$_]->[1] eq $tag_name) {
3096 wakaba 1.79 !!!cp ('t51');
3097 wakaba 1.1 $formatting_element = $active_formatting_elements->[$_];
3098     $formatting_element_i_in_active = $_;
3099     last AFE;
3100     } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {
3101 wakaba 1.79 !!!cp ('t52');
3102 wakaba 1.1 last AFE;
3103     }
3104     } # AFE
3105     unless (defined $formatting_element) {
3106 wakaba 1.79 !!!cp ('t53');
3107 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);
3108 wakaba 1.1 ## Ignore the token
3109     !!!next-token;
3110     return;
3111     }
3112     ## has an element in scope
3113     my $in_scope = 1;
3114     my $formatting_element_i_in_open;
3115 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3116     my $node = $self->{open_elements}->[$_];
3117 wakaba 1.1 if ($node->[0] eq $formatting_element->[0]) {
3118     if ($in_scope) {
3119 wakaba 1.79 !!!cp ('t54');
3120 wakaba 1.1 $formatting_element_i_in_open = $_;
3121     last INSCOPE;
3122     } else { # in open elements but not in scope
3123 wakaba 1.79 !!!cp ('t55');
3124 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},
3125     token => $end_tag_token);
3126 wakaba 1.1 ## Ignore the token
3127     !!!next-token;
3128     return;
3129     }
3130     } elsif ({
3131 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
3132 wakaba 1.1 button => 1, marquee => 1, object => 1, html => 1,
3133     }->{$node->[1]}) {
3134 wakaba 1.79 !!!cp ('t56');
3135 wakaba 1.1 $in_scope = 0;
3136     }
3137     } # INSCOPE
3138     unless (defined $formatting_element_i_in_open) {
3139 wakaba 1.79 !!!cp ('t57');
3140 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},
3141     token => $end_tag_token);
3142 wakaba 1.1 pop @$active_formatting_elements; # $formatting_element
3143     !!!next-token; ## TODO: ok?
3144     return;
3145     }
3146 wakaba 1.3 if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
3147 wakaba 1.79 !!!cp ('t58');
3148 wakaba 1.122 !!!parse-error (type => 'not closed',
3149     value => $self->{open_elements}->[-1]->[0]
3150     ->manakai_local_name,
3151 wakaba 1.113 token => $end_tag_token);
3152 wakaba 1.1 }
3153    
3154     ## Step 2
3155     my $furthest_block;
3156     my $furthest_block_i_in_open;
3157 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
3158     my $node = $self->{open_elements}->[$_];
3159 wakaba 1.1 if (not $formatting_category->{$node->[1]} and
3160     #not $phrasing_category->{$node->[1]} and
3161     ($special_category->{$node->[1]} or
3162 wakaba 1.103 $scoping_category->{$node->[1]})) { ## Scoping is redundant, maybe
3163 wakaba 1.79 !!!cp ('t59');
3164 wakaba 1.1 $furthest_block = $node;
3165     $furthest_block_i_in_open = $_;
3166     } elsif ($node->[0] eq $formatting_element->[0]) {
3167 wakaba 1.79 !!!cp ('t60');
3168 wakaba 1.1 last OE;
3169     }
3170     } # OE
3171    
3172     ## Step 3
3173     unless (defined $furthest_block) { # MUST
3174 wakaba 1.79 !!!cp ('t61');
3175 wakaba 1.3 splice @{$self->{open_elements}}, $formatting_element_i_in_open;
3176 wakaba 1.1 splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
3177     !!!next-token;
3178     return;
3179     }
3180    
3181     ## Step 4
3182 wakaba 1.3 my $common_ancestor_node = $self->{open_elements}->[$formatting_element_i_in_open - 1];
3183 wakaba 1.1
3184     ## Step 5
3185     my $furthest_block_parent = $furthest_block->[0]->parent_node;
3186     if (defined $furthest_block_parent) {
3187 wakaba 1.79 !!!cp ('t62');
3188 wakaba 1.1 $furthest_block_parent->remove_child ($furthest_block->[0]);
3189     }
3190    
3191     ## Step 6
3192     my $bookmark_prev_el
3193     = $active_formatting_elements->[$formatting_element_i_in_active - 1]
3194     ->[0];
3195    
3196     ## Step 7
3197     my $node = $furthest_block;
3198     my $node_i_in_open = $furthest_block_i_in_open;
3199     my $last_node = $furthest_block;
3200     S7: {
3201     ## Step 1
3202     $node_i_in_open--;
3203 wakaba 1.3 $node = $self->{open_elements}->[$node_i_in_open];
3204 wakaba 1.1
3205     ## Step 2
3206     my $node_i_in_active;
3207     S7S2: {
3208     for (reverse 0..$#$active_formatting_elements) {
3209     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
3210 wakaba 1.79 !!!cp ('t63');
3211 wakaba 1.1 $node_i_in_active = $_;
3212     last S7S2;
3213     }
3214     }
3215 wakaba 1.3 splice @{$self->{open_elements}}, $node_i_in_open, 1;
3216 wakaba 1.1 redo S7;
3217     } # S7S2
3218    
3219     ## Step 3
3220     last S7 if $node->[0] eq $formatting_element->[0];
3221    
3222     ## Step 4
3223     if ($last_node->[0] eq $furthest_block->[0]) {
3224 wakaba 1.79 !!!cp ('t64');
3225 wakaba 1.1 $bookmark_prev_el = $node->[0];
3226     }
3227    
3228     ## Step 5
3229     if ($node->[0]->has_child_nodes ()) {
3230 wakaba 1.79 !!!cp ('t65');
3231 wakaba 1.1 my $clone = [$node->[0]->clone_node (0), $node->[1]];
3232     $active_formatting_elements->[$node_i_in_active] = $clone;
3233 wakaba 1.3 $self->{open_elements}->[$node_i_in_open] = $clone;
3234 wakaba 1.1 $node = $clone;
3235     }
3236    
3237     ## Step 6
3238     $node->[0]->append_child ($last_node->[0]);
3239    
3240     ## Step 7
3241     $last_node = $node;
3242    
3243     ## Step 8
3244     redo S7;
3245     } # S7
3246    
3247     ## Step 8
3248 wakaba 1.102 if ({
3249     table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,
3250     }->{$common_ancestor_node->[1]}) {
3251     my $foster_parent_element;
3252     my $next_sibling;
3253     OE: for (reverse 0..$#{$self->{open_elements}}) {
3254     if ($self->{open_elements}->[$_]->[1] eq 'table') {
3255     my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3256     if (defined $parent and $parent->node_type == 1) {
3257     !!!cp ('t65.1');
3258     $foster_parent_element = $parent;
3259     $next_sibling = $self->{open_elements}->[$_]->[0];
3260     } else {
3261     !!!cp ('t65.2');
3262     $foster_parent_element
3263     = $self->{open_elements}->[$_ - 1]->[0];
3264     }
3265     last OE;
3266     }
3267     } # OE
3268     $foster_parent_element = $self->{open_elements}->[0]->[0]
3269     unless defined $foster_parent_element;
3270     $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
3271     $open_tables->[-1]->[1] = 1; # tainted
3272     } else {
3273     !!!cp ('t65.3');
3274     $common_ancestor_node->[0]->append_child ($last_node->[0]);
3275     }
3276 wakaba 1.1
3277     ## Step 9
3278     my $clone = [$formatting_element->[0]->clone_node (0),
3279     $formatting_element->[1]];
3280    
3281     ## Step 10
3282     my @cn = @{$furthest_block->[0]->child_nodes};
3283     $clone->[0]->append_child ($_) for @cn;
3284    
3285     ## Step 11
3286     $furthest_block->[0]->append_child ($clone->[0]);
3287    
3288     ## Step 12
3289     my $i;
3290     AFE: for (reverse 0..$#$active_formatting_elements) {
3291     if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
3292 wakaba 1.79 !!!cp ('t66');
3293 wakaba 1.1 splice @$active_formatting_elements, $_, 1;
3294     $i-- and last AFE if defined $i;
3295     } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
3296 wakaba 1.79 !!!cp ('t67');
3297 wakaba 1.1 $i = $_;
3298     }
3299     } # AFE
3300     splice @$active_formatting_elements, $i + 1, 0, $clone;
3301    
3302     ## Step 13
3303     undef $i;
3304 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
3305     if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
3306 wakaba 1.79 !!!cp ('t68');
3307 wakaba 1.3 splice @{$self->{open_elements}}, $_, 1;
3308 wakaba 1.1 $i-- and last OE if defined $i;
3309 wakaba 1.3 } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
3310 wakaba 1.79 !!!cp ('t69');
3311 wakaba 1.1 $i = $_;
3312     }
3313     } # OE
3314 wakaba 1.3 splice @{$self->{open_elements}}, $i + 1, 1, $clone;
3315 wakaba 1.1
3316     ## Step 14
3317     redo FET;
3318     } # FET
3319     }; # $formatting_end_tag
3320    
3321 wakaba 1.96 $insert = my $insert_to_current = sub {
3322 wakaba 1.25 $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
3323 wakaba 1.1 }; # $insert_to_current
3324    
3325     my $insert_to_foster = sub {
3326 wakaba 1.95 my $child = shift;
3327     if ({
3328     table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,
3329     }->{$self->{open_elements}->[-1]->[1]}) {
3330     # MUST
3331     my $foster_parent_element;
3332     my $next_sibling;
3333 wakaba 1.3 OE: for (reverse 0..$#{$self->{open_elements}}) {
3334     if ($self->{open_elements}->[$_]->[1] eq 'table') {
3335     my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3336 wakaba 1.1 if (defined $parent and $parent->node_type == 1) {
3337 wakaba 1.79 !!!cp ('t70');
3338 wakaba 1.1 $foster_parent_element = $parent;
3339 wakaba 1.3 $next_sibling = $self->{open_elements}->[$_]->[0];
3340 wakaba 1.1 } else {
3341 wakaba 1.79 !!!cp ('t71');
3342 wakaba 1.1 $foster_parent_element
3343 wakaba 1.3 = $self->{open_elements}->[$_ - 1]->[0];
3344 wakaba 1.1 }
3345     last OE;
3346     }
3347     } # OE
3348 wakaba 1.3 $foster_parent_element = $self->{open_elements}->[0]->[0]
3349 wakaba 1.1 unless defined $foster_parent_element;
3350     $foster_parent_element->insert_before
3351     ($child, $next_sibling);
3352 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
3353     } else {
3354     !!!cp ('t72');
3355     $self->{open_elements}->[-1]->[0]->append_child ($child);
3356     }
3357 wakaba 1.1 }; # $insert_to_foster
3358    
3359 wakaba 1.52 B: {
3360 wakaba 1.55 if ($token->{type} == DOCTYPE_TOKEN) {
3361 wakaba 1.79 !!!cp ('t73');
3362 wakaba 1.113 !!!parse-error (type => 'DOCTYPE in the middle', token => $token);
3363 wakaba 1.52 ## Ignore the token
3364     ## Stay in the phase
3365     !!!next-token;
3366     redo B;
3367 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN and
3368 wakaba 1.52 $token->{tag_name} eq 'html') {
3369 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
3370 wakaba 1.79 !!!cp ('t79');
3371 wakaba 1.113 !!!parse-error (type => 'after html:html', token => $token);
3372 wakaba 1.54 $self->{insertion_mode} = AFTER_BODY_IM;
3373     } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
3374 wakaba 1.79 !!!cp ('t80');
3375 wakaba 1.113 !!!parse-error (type => 'after html:html', token => $token);
3376 wakaba 1.54 $self->{insertion_mode} = AFTER_FRAMESET_IM;
3377 wakaba 1.79 } else {
3378     !!!cp ('t81');
3379 wakaba 1.52 }
3380    
3381 wakaba 1.84 !!!cp ('t82');
3382 wakaba 1.113 !!!parse-error (type => 'not first start tag', token => $token);
3383 wakaba 1.52 my $top_el = $self->{open_elements}->[0]->[0];
3384     for my $attr_name (keys %{$token->{attributes}}) {
3385     unless ($top_el->has_attribute_ns (undef, $attr_name)) {
3386 wakaba 1.79 !!!cp ('t84');
3387 wakaba 1.52 $top_el->set_attribute_ns
3388     (undef, [undef, $attr_name],
3389     $token->{attributes}->{$attr_name}->{value});
3390     }
3391     }
3392     !!!next-token;
3393     redo B;
3394 wakaba 1.55 } elsif ($token->{type} == COMMENT_TOKEN) {
3395 wakaba 1.52 my $comment = $self->{document}->create_comment ($token->{data});
3396 wakaba 1.56 if ($self->{insertion_mode} & AFTER_HTML_IMS) {
3397 wakaba 1.79 !!!cp ('t85');
3398 wakaba 1.52 $self->{document}->append_child ($comment);
3399 wakaba 1.54 } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
3400 wakaba 1.79 !!!cp ('t86');
3401 wakaba 1.52 $self->{open_elements}->[0]->[0]->append_child ($comment);
3402     } else {
3403 wakaba 1.79 !!!cp ('t87');
3404 wakaba 1.52 $self->{open_elements}->[-1]->[0]->append_child ($comment);
3405     }
3406     !!!next-token;
3407     redo B;
3408 wakaba 1.56 } elsif ($self->{insertion_mode} & HEAD_IMS) {
3409 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
3410 wakaba 1.52 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3411 wakaba 1.99 unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3412     !!!cp ('t88.2');
3413     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3414     } else {
3415     !!!cp ('t88.1');
3416     ## Ignore the token.
3417     !!!next-token;
3418     redo B;
3419     }
3420 wakaba 1.52 unless (length $token->{data}) {
3421 wakaba 1.79 !!!cp ('t88');
3422 wakaba 1.52 !!!next-token;
3423     redo B;
3424 wakaba 1.1 }
3425     }
3426 wakaba 1.52
3427 wakaba 1.54 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3428 wakaba 1.79 !!!cp ('t89');
3429 wakaba 1.52 ## As if <head>
3430 wakaba 1.116 !!!create-element ($self->{head_element}, 'head',, $token);
3431 wakaba 1.52 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3432     push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3433    
3434     ## Reprocess in the "in head" insertion mode...
3435     pop @{$self->{open_elements}};
3436    
3437     ## Reprocess in the "after head" insertion mode...
3438 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3439 wakaba 1.79 !!!cp ('t90');
3440 wakaba 1.52 ## As if </noscript>
3441     pop @{$self->{open_elements}};
3442 wakaba 1.113 !!!parse-error (type => 'in noscript:#character', token => $token);
3443 wakaba 1.1
3444 wakaba 1.52 ## Reprocess in the "in head" insertion mode...
3445     ## As if </head>
3446     pop @{$self->{open_elements}};
3447    
3448     ## Reprocess in the "after head" insertion mode...
3449 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3450 wakaba 1.79 !!!cp ('t91');
3451 wakaba 1.52 pop @{$self->{open_elements}};
3452    
3453     ## Reprocess in the "after head" insertion mode...
3454 wakaba 1.79 } else {
3455     !!!cp ('t92');
3456 wakaba 1.1 }
3457 wakaba 1.52
3458     ## "after head" insertion mode
3459     ## As if <body>
3460 wakaba 1.116 !!!insert-element ('body',, $token);
3461 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
3462 wakaba 1.52 ## reprocess
3463     redo B;
3464 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
3465 wakaba 1.52 if ($token->{tag_name} eq 'head') {
3466 wakaba 1.54 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3467 wakaba 1.79 !!!cp ('t93');
3468 wakaba 1.116 !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes}, $token);
3469 wakaba 1.52 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3470     push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];
3471 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
3472 wakaba 1.52 !!!next-token;
3473     redo B;
3474 wakaba 1.54 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3475 wakaba 1.79 !!!cp ('t94');
3476 wakaba 1.54 #
3477     } else {
3478 wakaba 1.79 !!!cp ('t95');
3479 wakaba 1.113 !!!parse-error (type => 'in head:head', token => $token); # or in head noscript
3480 wakaba 1.52 ## Ignore the token
3481     !!!next-token;
3482     redo B;
3483     }
3484 wakaba 1.54 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3485 wakaba 1.79 !!!cp ('t96');
3486 wakaba 1.52 ## As if <head>
3487 wakaba 1.116 !!!create-element ($self->{head_element}, 'head',, $token);
3488 wakaba 1.52 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3489     push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3490    
3491 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
3492 wakaba 1.52 ## Reprocess in the "in head" insertion mode...
3493 wakaba 1.79 } else {
3494     !!!cp ('t97');
3495 wakaba 1.1 }
3496 wakaba 1.52
3497 wakaba 1.49 if ($token->{tag_name} eq 'base') {
3498 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3499 wakaba 1.79 !!!cp ('t98');
3500 wakaba 1.49 ## As if </noscript>
3501     pop @{$self->{open_elements}};
3502 wakaba 1.113 !!!parse-error (type => 'in noscript:base', token => $token);
3503 wakaba 1.49
3504 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
3505 wakaba 1.49 ## Reprocess in the "in head" insertion mode...
3506 wakaba 1.79 } else {
3507     !!!cp ('t99');
3508 wakaba 1.49 }
3509    
3510     ## NOTE: There is a "as if in head" code clone.
3511 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3512 wakaba 1.79 !!!cp ('t100');
3513 wakaba 1.113 !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3514 wakaba 1.49 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3515 wakaba 1.79 } else {
3516     !!!cp ('t101');
3517 wakaba 1.49 }
3518 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3519 wakaba 1.49 pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3520 wakaba 1.100 pop @{$self->{open_elements}} # <head>
3521 wakaba 1.54 if $self->{insertion_mode} == AFTER_HEAD_IM;
3522 wakaba 1.49 !!!next-token;
3523     redo B;
3524     } elsif ($token->{tag_name} eq 'link') {
3525 wakaba 1.25 ## NOTE: There is a "as if in head" code clone.
3526 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3527 wakaba 1.79 !!!cp ('t102');
3528 wakaba 1.113 !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3529 wakaba 1.25 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3530 wakaba 1.79 } else {
3531     !!!cp ('t103');
3532 wakaba 1.25 }
3533 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3534 wakaba 1.25 pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3535 wakaba 1.100 pop @{$self->{open_elements}} # <head>
3536 wakaba 1.54 if $self->{insertion_mode} == AFTER_HEAD_IM;
3537 wakaba 1.1 !!!next-token;
3538 wakaba 1.25 redo B;
3539 wakaba 1.34 } elsif ($token->{tag_name} eq 'meta') {
3540     ## NOTE: There is a "as if in head" code clone.
3541 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3542 wakaba 1.79 !!!cp ('t104');
3543 wakaba 1.113 !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3544 wakaba 1.34 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3545 wakaba 1.79 } else {
3546     !!!cp ('t105');
3547 wakaba 1.34 }
3548 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3549 wakaba 1.66 my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3550 wakaba 1.34
3551     unless ($self->{confident}) {
3552     if ($token->{attributes}->{charset}) { ## TODO: And if supported
3553 wakaba 1.79 !!!cp ('t106');
3554 wakaba 1.63 $self->{change_encoding}
3555 wakaba 1.114 ->($self, $token->{attributes}->{charset}->{value},
3556     $token);
3557 wakaba 1.66
3558     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
3559     ->set_user_data (manakai_has_reference =>
3560     $token->{attributes}->{charset}
3561     ->{has_reference});
3562 wakaba 1.63 } elsif ($token->{attributes}->{content}) {
3563 wakaba 1.35 ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.
3564 wakaba 1.63 if ($token->{attributes}->{content}->{value}
3565 wakaba 1.70 =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
3566     [\x09-\x0D\x20]*=
3567 wakaba 1.34 [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
3568     ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
3569 wakaba 1.79 !!!cp ('t107');
3570 wakaba 1.63 $self->{change_encoding}
3571 wakaba 1.114 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
3572     $token);
3573 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
3574     ->set_user_data (manakai_has_reference =>
3575     $token->{attributes}->{content}
3576     ->{has_reference});
3577 wakaba 1.79 } else {
3578     !!!cp ('t108');
3579 wakaba 1.63 }
3580 wakaba 1.34 }
3581 wakaba 1.66 } else {
3582     if ($token->{attributes}->{charset}) {
3583 wakaba 1.79 !!!cp ('t109');
3584 wakaba 1.66 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
3585     ->set_user_data (manakai_has_reference =>
3586     $token->{attributes}->{charset}
3587     ->{has_reference});
3588     }
3589 wakaba 1.68 if ($token->{attributes}->{content}) {
3590 wakaba 1.79 !!!cp ('t110');
3591 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
3592     ->set_user_data (manakai_has_reference =>
3593     $token->{attributes}->{content}
3594     ->{has_reference});
3595     }
3596 wakaba 1.34 }
3597    
3598 wakaba 1.100 pop @{$self->{open_elements}} # <head>
3599 wakaba 1.54 if $self->{insertion_mode} == AFTER_HEAD_IM;
3600 wakaba 1.34 !!!next-token;
3601     redo B;
3602 wakaba 1.49 } elsif ($token->{tag_name} eq 'title') {
3603 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3604 wakaba 1.79 !!!cp ('t111');
3605 wakaba 1.49 ## As if </noscript>
3606     pop @{$self->{open_elements}};
3607 wakaba 1.113 !!!parse-error (type => 'in noscript:title', token => $token);
3608 wakaba 1.49
3609 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
3610 wakaba 1.49 ## Reprocess in the "in head" insertion mode...
3611 wakaba 1.54 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3612 wakaba 1.79 !!!cp ('t112');
3613 wakaba 1.113 !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3614 wakaba 1.25 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3615 wakaba 1.79 } else {
3616     !!!cp ('t113');
3617 wakaba 1.25 }
3618 wakaba 1.49
3619     ## NOTE: There is a "as if in head" code clone.
3620 wakaba 1.31 my $parent = defined $self->{head_element} ? $self->{head_element}
3621     : $self->{open_elements}->[-1]->[0];
3622 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
3623 wakaba 1.100 pop @{$self->{open_elements}} # <head>
3624 wakaba 1.54 if $self->{insertion_mode} == AFTER_HEAD_IM;
3625 wakaba 1.25 redo B;
3626     } elsif ($token->{tag_name} eq 'style') {
3627     ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
3628 wakaba 1.54 ## insertion mode IN_HEAD_IM)
3629 wakaba 1.25 ## NOTE: There is a "as if in head" code clone.
3630 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3631 wakaba 1.79 !!!cp ('t114');
3632 wakaba 1.113 !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3633 wakaba 1.25 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3634 wakaba 1.79 } else {
3635     !!!cp ('t115');
3636 wakaba 1.25 }
3637 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
3638 wakaba 1.100 pop @{$self->{open_elements}} # <head>
3639 wakaba 1.54 if $self->{insertion_mode} == AFTER_HEAD_IM;
3640 wakaba 1.25 redo B;
3641     } elsif ($token->{tag_name} eq 'noscript') {
3642 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_IM) {
3643 wakaba 1.79 !!!cp ('t116');
3644 wakaba 1.25 ## NOTE: and scripting is disalbed
3645 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3646 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
3647 wakaba 1.1 !!!next-token;
3648 wakaba 1.25 redo B;
3649 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3650 wakaba 1.79 !!!cp ('t117');
3651 wakaba 1.113 !!!parse-error (type => 'in noscript:noscript', token => $token);
3652 wakaba 1.1 ## Ignore the token
3653 wakaba 1.41 !!!next-token;
3654 wakaba 1.25 redo B;
3655 wakaba 1.1 } else {
3656 wakaba 1.79 !!!cp ('t118');
3657 wakaba 1.25 #
3658 wakaba 1.1 }
3659 wakaba 1.49 } elsif ($token->{tag_name} eq 'script') {
3660 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3661 wakaba 1.79 !!!cp ('t119');
3662 wakaba 1.49 ## As if </noscript>
3663     pop @{$self->{open_elements}};
3664 wakaba 1.113 !!!parse-error (type => 'in noscript:script', token => $token);
3665 wakaba 1.49
3666 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
3667 wakaba 1.49 ## Reprocess in the "in head" insertion mode...
3668 wakaba 1.54 } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3669 wakaba 1.79 !!!cp ('t120');
3670 wakaba 1.113 !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3671 wakaba 1.25 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3672 wakaba 1.79 } else {
3673     !!!cp ('t121');
3674 wakaba 1.25 }
3675 wakaba 1.49
3676 wakaba 1.25 ## NOTE: There is a "as if in head" code clone.
3677 wakaba 1.100 $script_start_tag->();
3678     pop @{$self->{open_elements}} # <head>
3679 wakaba 1.54 if $self->{insertion_mode} == AFTER_HEAD_IM;
3680 wakaba 1.1 redo B;
3681 wakaba 1.49 } elsif ($token->{tag_name} eq 'body' or
3682 wakaba 1.25 $token->{tag_name} eq 'frameset') {
3683 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3684 wakaba 1.79 !!!cp ('t122');
3685 wakaba 1.49 ## As if </noscript>
3686     pop @{$self->{open_elements}};
3687 wakaba 1.113 !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);
3688 wakaba 1.49
3689     ## Reprocess in the "in head" insertion mode...
3690     ## As if </head>
3691     pop @{$self->{open_elements}};
3692    
3693     ## Reprocess in the "after head" insertion mode...
3694 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3695 wakaba 1.79 !!!cp ('t124');
3696 wakaba 1.49 pop @{$self->{open_elements}};
3697    
3698     ## Reprocess in the "after head" insertion mode...
3699 wakaba 1.79 } else {
3700     !!!cp ('t125');
3701 wakaba 1.49 }
3702    
3703     ## "after head" insertion mode
3704 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3705 wakaba 1.54 if ($token->{tag_name} eq 'body') {
3706 wakaba 1.79 !!!cp ('t126');
3707 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
3708     } elsif ($token->{tag_name} eq 'frameset') {
3709 wakaba 1.79 !!!cp ('t127');
3710 wakaba 1.54 $self->{insertion_mode} = IN_FRAMESET_IM;
3711     } else {
3712     die "$0: tag name: $self->{tag_name}";
3713     }
3714 wakaba 1.1 !!!next-token;
3715     redo B;
3716     } else {
3717 wakaba 1.79 !!!cp ('t128');
3718 wakaba 1.1 #
3719     }
3720 wakaba 1.49
3721 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3722 wakaba 1.79 !!!cp ('t129');
3723 wakaba 1.49 ## As if </noscript>
3724     pop @{$self->{open_elements}};
3725 wakaba 1.113 !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);
3726 wakaba 1.49
3727     ## Reprocess in the "in head" insertion mode...
3728     ## As if </head>
3729 wakaba 1.25 pop @{$self->{open_elements}};
3730 wakaba 1.49
3731     ## Reprocess in the "after head" insertion mode...
3732 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3733 wakaba 1.79 !!!cp ('t130');
3734 wakaba 1.49 ## As if </head>
3735 wakaba 1.25 pop @{$self->{open_elements}};
3736 wakaba 1.49
3737     ## Reprocess in the "after head" insertion mode...
3738 wakaba 1.79 } else {
3739     !!!cp ('t131');
3740 wakaba 1.49 }
3741    
3742     ## "after head" insertion mode
3743     ## As if <body>
3744 wakaba 1.116 !!!insert-element ('body',, $token);
3745 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
3746 wakaba 1.49 ## reprocess
3747     redo B;
3748 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
3749 wakaba 1.49 if ($token->{tag_name} eq 'head') {
3750 wakaba 1.54 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3751 wakaba 1.79 !!!cp ('t132');
3752 wakaba 1.50 ## As if <head>
3753 wakaba 1.116 !!!create-element ($self->{head_element}, 'head',, $token);
3754 wakaba 1.50 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3755     push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3756    
3757     ## Reprocess in the "in head" insertion mode...
3758     pop @{$self->{open_elements}};
3759 wakaba 1.54 $self->{insertion_mode} = AFTER_HEAD_IM;
3760 wakaba 1.50 !!!next-token;
3761     redo B;
3762 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3763 wakaba 1.79 !!!cp ('t133');
3764 wakaba 1.49 ## As if </noscript>
3765     pop @{$self->{open_elements}};
3766 wakaba 1.113 !!!parse-error (type => 'in noscript:/head', token => $token);
3767 wakaba 1.49
3768     ## Reprocess in the "in head" insertion mode...
3769 wakaba 1.50 pop @{$self->{open_elements}};
3770 wakaba 1.54 $self->{insertion_mode} = AFTER_HEAD_IM;
3771 wakaba 1.50 !!!next-token;
3772     redo B;
3773 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3774 wakaba 1.79 !!!cp ('t134');
3775 wakaba 1.49 pop @{$self->{open_elements}};
3776 wakaba 1.54 $self->{insertion_mode} = AFTER_HEAD_IM;
3777 wakaba 1.49 !!!next-token;
3778     redo B;
3779     } else {
3780 wakaba 1.79 !!!cp ('t135');
3781 wakaba 1.49 #
3782     }
3783     } elsif ($token->{tag_name} eq 'noscript') {
3784 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3785 wakaba 1.79 !!!cp ('t136');
3786 wakaba 1.49 pop @{$self->{open_elements}};
3787 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
3788 wakaba 1.49 !!!next-token;
3789     redo B;
3790 wakaba 1.54 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3791 wakaba 1.79 !!!cp ('t137');
3792 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:noscript', token => $token);
3793 wakaba 1.50 ## Ignore the token ## ISSUE: An issue in the spec.
3794     !!!next-token;
3795     redo B;
3796 wakaba 1.49 } else {
3797 wakaba 1.79 !!!cp ('t138');
3798 wakaba 1.49 #
3799     }
3800     } elsif ({
3801 wakaba 1.31 body => 1, html => 1,
3802     }->{$token->{tag_name}}) {
3803 wakaba 1.54 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3804 wakaba 1.79 !!!cp ('t139');
3805 wakaba 1.50 ## As if <head>
3806 wakaba 1.116 !!!create-element ($self->{head_element}, 'head',, $token);
3807 wakaba 1.50 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3808     push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3809    
3810 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
3811 wakaba 1.50 ## Reprocess in the "in head" insertion mode...
3812 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3813 wakaba 1.79 !!!cp ('t140');
3814 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
3815 wakaba 1.49 ## Ignore the token
3816     !!!next-token;
3817     redo B;
3818 wakaba 1.79 } else {
3819     !!!cp ('t141');
3820 wakaba 1.49 }
3821 wakaba 1.50
3822     #
3823 wakaba 1.49 } elsif ({
3824 wakaba 1.31 p => 1, br => 1,
3825     }->{$token->{tag_name}}) {
3826 wakaba 1.54 if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3827 wakaba 1.79 !!!cp ('t142');
3828 wakaba 1.50 ## As if <head>
3829 wakaba 1.116 !!!create-element ($self->{head_element}, 'head',, $token);
3830 wakaba 1.50 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3831     push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3832    
3833 wakaba 1.54 $self->{insertion_mode} = IN_HEAD_IM;
3834 wakaba 1.50 ## Reprocess in the "in head" insertion mode...
3835 wakaba 1.79 } else {
3836     !!!cp ('t143');
3837 wakaba 1.50 }
3838    
3839 wakaba 1.1 #
3840 wakaba 1.25 } else {
3841 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3842 wakaba 1.79 !!!cp ('t144');
3843 wakaba 1.54 #
3844     } else {
3845 wakaba 1.79 !!!cp ('t145');
3846 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
3847 wakaba 1.49 ## Ignore the token
3848     !!!next-token;
3849     redo B;
3850     }
3851     }
3852    
3853 wakaba 1.54 if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3854 wakaba 1.79 !!!cp ('t146');
3855 wakaba 1.49 ## As if </noscript>
3856     pop @{$self->{open_elements}};
3857 wakaba 1.113 !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);
3858 wakaba 1.49
3859     ## Reprocess in the "in head" insertion mode...
3860     ## As if </head>
3861     pop @{$self->{open_elements}};
3862    
3863     ## Reprocess in the "after head" insertion mode...
3864 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3865 wakaba 1.79 !!!cp ('t147');
3866 wakaba 1.49 ## As if </head>
3867     pop @{$self->{open_elements}};
3868    
3869     ## Reprocess in the "after head" insertion mode...
3870 wakaba 1.54 } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3871 wakaba 1.82 ## ISSUE: This case cannot be reached?
3872 wakaba 1.79 !!!cp ('t148');
3873 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
3874 wakaba 1.50 ## Ignore the token ## ISSUE: An issue in the spec.
3875     !!!next-token;
3876     redo B;
3877 wakaba 1.79 } else {
3878     !!!cp ('t149');
3879 wakaba 1.1 }
3880    
3881 wakaba 1.49 ## "after head" insertion mode
3882     ## As if <body>
3883 wakaba 1.116 !!!insert-element ('body',, $token);
3884 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
3885 wakaba 1.52 ## reprocess
3886     redo B;
3887 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3888     if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3889     !!!cp ('t149.1');
3890    
3891     ## NOTE: As if <head>
3892 wakaba 1.116 !!!create-element ($self->{head_element}, 'head',, $token);
3893 wakaba 1.104 $self->{open_elements}->[-1]->[0]->append_child
3894     ($self->{head_element});
3895     #push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3896     #$self->{insertion_mode} = IN_HEAD_IM;
3897     ## NOTE: Reprocess.
3898    
3899     ## NOTE: As if </head>
3900     #pop @{$self->{open_elements}};
3901     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
3902     ## NOTE: Reprocess.
3903    
3904     #
3905     } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3906     !!!cp ('t149.2');
3907    
3908     ## NOTE: As if </head>
3909     pop @{$self->{open_elements}};
3910     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
3911     ## NOTE: Reprocess.
3912    
3913     #
3914     } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3915     !!!cp ('t149.3');
3916    
3917 wakaba 1.113 !!!parse-error (type => 'in noscript:#eof', token => $token);
3918 wakaba 1.104
3919     ## As if </noscript>
3920     pop @{$self->{open_elements}};
3921     #$self->{insertion_mode} = IN_HEAD_IM;
3922     ## NOTE: Reprocess.
3923    
3924     ## NOTE: As if </head>
3925     pop @{$self->{open_elements}};
3926     #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
3927     ## NOTE: Reprocess.
3928    
3929     #
3930     } else {
3931     !!!cp ('t149.4');
3932     #
3933     }
3934    
3935     ## NOTE: As if <body>
3936 wakaba 1.116 !!!insert-element ('body',, $token);
3937 wakaba 1.104 $self->{insertion_mode} = IN_BODY_IM;
3938     ## NOTE: Reprocess.
3939     redo B;
3940     } else {
3941     die "$0: $token->{type}: Unknown token type";
3942     }
3943 wakaba 1.52
3944     ## ISSUE: An issue in the spec.
3945 wakaba 1.56 } elsif ($self->{insertion_mode} & BODY_IMS) {
3946 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
3947 wakaba 1.79 !!!cp ('t150');
3948 wakaba 1.52 ## NOTE: There is a code clone of "character in body".
3949     $reconstruct_active_formatting_elements->($insert_to_current);
3950    
3951     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3952    
3953     !!!next-token;
3954     redo B;
3955 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
3956 wakaba 1.52 if ({
3957     caption => 1, col => 1, colgroup => 1, tbody => 1,
3958     td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
3959     }->{$token->{tag_name}}) {
3960 wakaba 1.54 if ($self->{insertion_mode} == IN_CELL_IM) {
3961 wakaba 1.52 ## have an element in table scope
3962 wakaba 1.108 for (reverse 0..$#{$self->{open_elements}}) {
3963 wakaba 1.52 my $node = $self->{open_elements}->[$_];
3964     if ($node->[1] eq 'td' or $node->[1] eq 'th') {
3965 wakaba 1.79 !!!cp ('t151');
3966 wakaba 1.108
3967     ## Close the cell
3968     !!!back-token; # <?>
3969 wakaba 1.122 $token = {type => END_TAG_TOKEN,
3970     tag_name => $node->[0]->manakai_local_name,
3971 wakaba 1.114 line => $token->{line},
3972     column => $token->{column}};
3973 wakaba 1.108 redo B;
3974 wakaba 1.52 } elsif ({
3975     table => 1, html => 1,
3976     }->{$node->[1]}) {
3977 wakaba 1.79 !!!cp ('t152');
3978 wakaba 1.108 ## ISSUE: This case can never be reached, maybe.
3979     last;
3980 wakaba 1.52 }
3981 wakaba 1.108 }
3982    
3983     !!!cp ('t153');
3984     !!!parse-error (type => 'start tag not allowed',
3985 wakaba 1.113 value => $token->{tag_name}, token => $token);
3986 wakaba 1.108 ## Ignore the token
3987     !!!next-token;
3988 wakaba 1.52 redo B;
3989 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
3990 wakaba 1.113 !!!parse-error (type => 'not closed:caption', token => $token);
3991 wakaba 1.52
3992 wakaba 1.108 ## NOTE: As if </caption>.
3993 wakaba 1.52 ## have a table element in table scope
3994     my $i;
3995 wakaba 1.108 INSCOPE: {
3996     for (reverse 0..$#{$self->{open_elements}}) {
3997     my $node = $self->{open_elements}->[$_];
3998     if ($node->[1] eq 'caption') {
3999     !!!cp ('t155');
4000     $i = $_;
4001     last INSCOPE;
4002     } elsif ({
4003     table => 1, html => 1,
4004     }->{$node->[1]}) {
4005     !!!cp ('t156');
4006     last;
4007     }
4008 wakaba 1.52 }
4009 wakaba 1.108
4010     !!!cp ('t157');
4011     !!!parse-error (type => 'start tag not allowed',
4012 wakaba 1.113 value => $token->{tag_name}, token => $token);
4013 wakaba 1.108 ## Ignore the token
4014     !!!next-token;
4015     redo B;
4016 wakaba 1.52 } # INSCOPE
4017    
4018     ## generate implied end tags
4019 wakaba 1.86 while ({
4020     dd => 1, dt => 1, li => 1, p => 1,
4021     }->{$self->{open_elements}->[-1]->[1]}) {
4022 wakaba 1.79 !!!cp ('t158');
4023 wakaba 1.86 pop @{$self->{open_elements}};
4024 wakaba 1.52 }
4025    
4026     if ($self->{open_elements}->[-1]->[1] ne 'caption') {
4027 wakaba 1.79 !!!cp ('t159');
4028 wakaba 1.122 !!!parse-error (type => 'not closed',
4029     value => $self->{open_elements}->[-1]->[0]
4030     ->manakai_local_name,
4031     token => $token);
4032 wakaba 1.79 } else {
4033     !!!cp ('t160');
4034 wakaba 1.52 }
4035    
4036     splice @{$self->{open_elements}}, $i;
4037    
4038     $clear_up_to_marker->();
4039    
4040 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
4041 wakaba 1.52
4042     ## reprocess
4043     redo B;
4044     } else {
4045 wakaba 1.79 !!!cp ('t161');
4046 wakaba 1.52 #
4047     }
4048     } else {
4049 wakaba 1.79 !!!cp ('t162');
4050 wakaba 1.52 #
4051     }
4052 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
4053 wakaba 1.52 if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
4054 wakaba 1.54 if ($self->{insertion_mode} == IN_CELL_IM) {
4055 wakaba 1.43 ## have an element in table scope
4056 wakaba 1.52 my $i;
4057 wakaba 1.43 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4058     my $node = $self->{open_elements}->[$_];
4059 wakaba 1.52 if ($node->[1] eq $token->{tag_name}) {
4060 wakaba 1.79 !!!cp ('t163');
4061 wakaba 1.52 $i = $_;
4062 wakaba 1.43 last INSCOPE;
4063     } elsif ({
4064     table => 1, html => 1,
4065     }->{$node->[1]}) {
4066 wakaba 1.79 !!!cp ('t164');
4067 wakaba 1.43 last INSCOPE;
4068     }
4069     } # INSCOPE
4070 wakaba 1.52 unless (defined $i) {
4071 wakaba 1.79 !!!cp ('t165');
4072 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4073 wakaba 1.43 ## Ignore the token
4074     !!!next-token;
4075     redo B;
4076     }
4077    
4078 wakaba 1.52 ## generate implied end tags
4079 wakaba 1.86 while ({
4080     dd => 1, dt => 1, li => 1, p => 1,
4081     }->{$self->{open_elements}->[-1]->[1]}) {
4082 wakaba 1.79 !!!cp ('t166');
4083 wakaba 1.86 pop @{$self->{open_elements}};
4084 wakaba 1.52 }
4085 wakaba 1.86
4086 wakaba 1.52 if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
4087 wakaba 1.79 !!!cp ('t167');
4088 wakaba 1.122 !!!parse-error (type => 'not closed',
4089     value => $self->{open_elements}->[-1]->[0]
4090     ->manakai_local_name,
4091     token => $token);
4092 wakaba 1.79 } else {
4093     !!!cp ('t168');
4094 wakaba 1.52 }
4095    
4096     splice @{$self->{open_elements}}, $i;
4097    
4098     $clear_up_to_marker->();
4099    
4100 wakaba 1.54 $self->{insertion_mode} = IN_ROW_IM;
4101 wakaba 1.52
4102     !!!next-token;
4103 wakaba 1.43 redo B;
4104 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
4105 wakaba 1.79 !!!cp ('t169');
4106 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4107 wakaba 1.52 ## Ignore the token
4108     !!!next-token;
4109     redo B;
4110     } else {
4111 wakaba 1.79 !!!cp ('t170');
4112 wakaba 1.52 #
4113     }
4114     } elsif ($token->{tag_name} eq 'caption') {
4115 wakaba 1.54 if ($self->{insertion_mode} == IN_CAPTION_IM) {
4116 wakaba 1.43 ## have a table element in table scope
4117     my $i;
4118 wakaba 1.108 INSCOPE: {
4119     for (reverse 0..$#{$self->{open_elements}}) {
4120     my $node = $self->{open_elements}->[$_];
4121     if ($node->[1] eq $token->{tag_name}) {
4122     !!!cp ('t171');
4123     $i = $_;
4124     last INSCOPE;
4125     } elsif ({
4126     table => 1, html => 1,
4127     }->{$node->[1]}) {
4128     !!!cp ('t172');
4129     last;
4130     }
4131 wakaba 1.43 }
4132 wakaba 1.108
4133     !!!cp ('t173');
4134     !!!parse-error (type => 'unmatched end tag',
4135 wakaba 1.113 value => $token->{tag_name}, token => $token);
4136 wakaba 1.108 ## Ignore the token
4137     !!!next-token;
4138     redo B;
4139 wakaba 1.43 } # INSCOPE
4140    
4141     ## generate implied end tags
4142 wakaba 1.86 while ({
4143     dd => 1, dt => 1, li => 1, p => 1,
4144     }->{$self->{open_elements}->[-1]->[1]}) {
4145 wakaba 1.79 !!!cp ('t174');
4146 wakaba 1.86 pop @{$self->{open_elements}};
4147 wakaba 1.43 }
4148 wakaba 1.52
4149     if ($self->{open_elements}->[-1]->[1] ne 'caption') {
4150 wakaba 1.79 !!!cp ('t175');
4151 wakaba 1.122 !!!parse-error (type => 'not closed',
4152     value => $self->{open_elements}->[-1]->[0]
4153     ->manakai_local_name,
4154     token => $token);
4155 wakaba 1.79 } else {
4156     !!!cp ('t176');
4157 wakaba 1.52 }
4158    
4159     splice @{$self->{open_elements}}, $i;
4160    
4161     $clear_up_to_marker->();
4162    
4163 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
4164 wakaba 1.52
4165     !!!next-token;
4166     redo B;
4167 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_CELL_IM) {
4168 wakaba 1.79 !!!cp ('t177');
4169 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4170 wakaba 1.52 ## Ignore the token
4171     !!!next-token;
4172     redo B;
4173     } else {
4174 wakaba 1.79 !!!cp ('t178');
4175 wakaba 1.52 #
4176     }
4177     } elsif ({
4178     table => 1, tbody => 1, tfoot => 1,
4179     thead => 1, tr => 1,
4180     }->{$token->{tag_name}} and
4181 wakaba 1.54 $self->{insertion_mode} == IN_CELL_IM) {
4182 wakaba 1.52 ## have an element in table scope
4183     my $i;
4184     my $tn;
4185 wakaba 1.108 INSCOPE: {
4186     for (reverse 0..$#{$self->{open_elements}}) {
4187     my $node = $self->{open_elements}->[$_];
4188     if ($node->[1] eq $token->{tag_name}) {
4189     !!!cp ('t179');
4190     $i = $_;
4191    
4192     ## Close the cell
4193     !!!back-token; # </?>
4194 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => $tn,
4195     line => $token->{line},
4196     column => $token->{column}};
4197 wakaba 1.108 redo B;
4198     } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {
4199     !!!cp ('t180');
4200     $tn = $node->[1];
4201     ## NOTE: There is exactly one |td| or |th| element
4202     ## in scope in the stack of open elements by definition.
4203     } elsif ({
4204     table => 1, html => 1,
4205     }->{$node->[1]}) {
4206     ## ISSUE: Can this be reached?
4207     !!!cp ('t181');
4208     last;
4209     }
4210 wakaba 1.52 }
4211 wakaba 1.108
4212 wakaba 1.79 !!!cp ('t182');
4213 wakaba 1.108 !!!parse-error (type => 'unmatched end tag',
4214 wakaba 1.113 value => $token->{tag_name}, token => $token);
4215 wakaba 1.52 ## Ignore the token
4216     !!!next-token;
4217     redo B;
4218 wakaba 1.108 } # INSCOPE
4219 wakaba 1.52 } elsif ($token->{tag_name} eq 'table' and
4220 wakaba 1.54 $self->{insertion_mode} == IN_CAPTION_IM) {
4221 wakaba 1.113 !!!parse-error (type => 'not closed:caption', token => $token);
4222 wakaba 1.52
4223     ## As if </caption>
4224     ## have a table element in table scope
4225     my $i;
4226     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4227     my $node = $self->{open_elements}->[$_];
4228     if ($node->[1] eq 'caption') {
4229 wakaba 1.79 !!!cp ('t184');
4230 wakaba 1.52 $i = $_;
4231     last INSCOPE;
4232     } elsif ({
4233     table => 1, html => 1,
4234     }->{$node->[1]}) {
4235 wakaba 1.79 !!!cp ('t185');
4236 wakaba 1.52 last INSCOPE;
4237     }
4238     } # INSCOPE
4239     unless (defined $i) {
4240 wakaba 1.79 !!!cp ('t186');
4241 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:caption', token => $token);
4242 wakaba 1.52 ## Ignore the token
4243     !!!next-token;
4244     redo B;
4245     }
4246    
4247     ## generate implied end tags
4248 wakaba 1.86 while ({
4249     dd => 1, dt => 1, li => 1, p => 1,
4250     }->{$self->{open_elements}->[-1]->[1]}) {
4251 wakaba 1.79 !!!cp ('t187');
4252 wakaba 1.86 pop @{$self->{open_elements}};
4253 wakaba 1.52 }
4254    
4255     if ($self->{open_elements}->[-1]->[1] ne 'caption') {
4256 wakaba 1.79 !!!cp ('t188');
4257 wakaba 1.122 !!!parse-error (type => 'not closed',
4258     value => $self->{open_elements}->[-1]->[0]
4259     ->manakai_local_name,
4260     token => $token);
4261 wakaba 1.79 } else {
4262     !!!cp ('t189');
4263 wakaba 1.52 }
4264    
4265     splice @{$self->{open_elements}}, $i;
4266    
4267     $clear_up_to_marker->();
4268    
4269 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
4270 wakaba 1.52
4271     ## reprocess
4272     redo B;
4273     } elsif ({
4274     body => 1, col => 1, colgroup => 1, html => 1,
4275     }->{$token->{tag_name}}) {
4276 wakaba 1.56 if ($self->{insertion_mode} & BODY_TABLE_IMS) {
4277 wakaba 1.79 !!!cp ('t190');
4278 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4279 wakaba 1.52 ## Ignore the token
4280     !!!next-token;
4281     redo B;
4282     } else {
4283 wakaba 1.79 !!!cp ('t191');
4284 wakaba 1.52 #
4285     }
4286     } elsif ({
4287     tbody => 1, tfoot => 1,
4288     thead => 1, tr => 1,
4289     }->{$token->{tag_name}} and
4290 wakaba 1.54 $self->{insertion_mode} == IN_CAPTION_IM) {
4291 wakaba 1.79 !!!cp ('t192');
4292 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4293 wakaba 1.52 ## Ignore the token
4294     !!!next-token;
4295     redo B;
4296     } else {
4297 wakaba 1.79 !!!cp ('t193');
4298 wakaba 1.52 #
4299     }
4300 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4301     for my $entry (@{$self->{open_elements}}) {
4302     if (not {
4303     dd => 1, dt => 1, li => 1, p => 1, tbody => 1, td => 1, tfoot => 1,
4304     th => 1, thead => 1, tr => 1, body => 1, html => 1,
4305     }->{$entry->[1]}) {
4306     !!!cp ('t75');
4307 wakaba 1.113 !!!parse-error (type => 'in body:#eof', token => $token);
4308 wakaba 1.104 last;
4309     }
4310     }
4311    
4312     ## Stop parsing.
4313     last B;
4314 wakaba 1.52 } else {
4315     die "$0: $token->{type}: Unknown token type";
4316     }
4317    
4318     $insert = $insert_to_current;
4319     #
4320 wakaba 1.56 } elsif ($self->{insertion_mode} & TABLE_IMS) {
4321 wakaba 1.58 if ($token->{type} == CHARACTER_TOKEN) {
4322 wakaba 1.95 if (not $open_tables->[-1]->[1] and # tainted
4323     $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4324     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4325 wakaba 1.52
4326 wakaba 1.95 unless (length $token->{data}) {
4327     !!!cp ('t194');
4328     !!!next-token;
4329     redo B;
4330     } else {
4331     !!!cp ('t195');
4332     }
4333     }
4334 wakaba 1.52
4335 wakaba 1.113 !!!parse-error (type => 'in table:#character', token => $token);
4336 wakaba 1.52
4337     ## As if in body, but insert into foster parent element
4338     ## ISSUE: Spec says that "whenever a node would be inserted
4339     ## into the current node" while characters might not be
4340     ## result in a new Text node.
4341     $reconstruct_active_formatting_elements->($insert_to_foster);
4342    
4343     if ({
4344     table => 1, tbody => 1, tfoot => 1,
4345     thead => 1, tr => 1,
4346     }->{$self->{open_elements}->[-1]->[1]}) {
4347     # MUST
4348     my $foster_parent_element;
4349     my $next_sibling;
4350     my $prev_sibling;
4351     OE: for (reverse 0..$#{$self->{open_elements}}) {
4352     if ($self->{open_elements}->[$_]->[1] eq 'table') {
4353     my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4354     if (defined $parent and $parent->node_type == 1) {
4355 wakaba 1.79 !!!cp ('t196');
4356 wakaba 1.52 $foster_parent_element = $parent;
4357     $next_sibling = $self->{open_elements}->[$_]->[0];
4358     $prev_sibling = $next_sibling->previous_sibling;
4359     } else {
4360 wakaba 1.79 !!!cp ('t197');
4361 wakaba 1.52 $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
4362     $prev_sibling = $foster_parent_element->last_child;
4363     }
4364     last OE;
4365     }
4366     } # OE
4367     $foster_parent_element = $self->{open_elements}->[0]->[0] and
4368     $prev_sibling = $foster_parent_element->last_child
4369     unless defined $foster_parent_element;
4370     if (defined $prev_sibling and
4371     $prev_sibling->node_type == 3) {
4372 wakaba 1.79 !!!cp ('t198');
4373 wakaba 1.52 $prev_sibling->manakai_append_text ($token->{data});
4374     } else {
4375 wakaba 1.79 !!!cp ('t199');
4376 wakaba 1.52 $foster_parent_element->insert_before
4377     ($self->{document}->create_text_node ($token->{data}),
4378     $next_sibling);
4379     }
4380 wakaba 1.95 $open_tables->[-1]->[1] = 1; # tainted
4381     } else {
4382     !!!cp ('t200');
4383     $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4384     }
4385 wakaba 1.52
4386 wakaba 1.95 !!!next-token;
4387     redo B;
4388 wakaba 1.58 } elsif ($token->{type} == START_TAG_TOKEN) {
4389 wakaba 1.52 if ({
4390 wakaba 1.54 tr => ($self->{insertion_mode} != IN_ROW_IM),
4391 wakaba 1.52 th => 1, td => 1,
4392     }->{$token->{tag_name}}) {
4393 wakaba 1.54 if ($self->{insertion_mode} == IN_TABLE_IM) {
4394 wakaba 1.52 ## Clear back to table context
4395     while ($self->{open_elements}->[-1]->[1] ne 'table' and
4396     $self->{open_elements}->[-1]->[1] ne 'html') {
4397 wakaba 1.79 !!!cp ('t201');
4398 wakaba 1.52 pop @{$self->{open_elements}};
4399 wakaba 1.43 }
4400    
4401 wakaba 1.116 !!!insert-element ('tbody',, $token);
4402 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
4403 wakaba 1.52 ## reprocess in the "in table body" insertion mode...
4404     }
4405    
4406 wakaba 1.54 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
4407 wakaba 1.52 unless ($token->{tag_name} eq 'tr') {
4408 wakaba 1.79 !!!cp ('t202');
4409 wakaba 1.113 !!!parse-error (type => 'missing start tag:tr', token => $token);
4410 wakaba 1.52 }
4411 wakaba 1.43
4412 wakaba 1.52 ## Clear back to table body context
4413     while (not {
4414     tbody => 1, tfoot => 1, thead => 1, html => 1,
4415     }->{$self->{open_elements}->[-1]->[1]}) {
4416 wakaba 1.79 !!!cp ('t203');
4417 wakaba 1.83 ## ISSUE: Can this case be reached?
4418 wakaba 1.52 pop @{$self->{open_elements}};
4419     }
4420 wakaba 1.43
4421 wakaba 1.54 $self->{insertion_mode} = IN_ROW_IM;
4422 wakaba 1.52 if ($token->{tag_name} eq 'tr') {
4423 wakaba 1.79 !!!cp ('t204');
4424 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4425 wakaba 1.52 !!!next-token;
4426     redo B;
4427     } else {
4428 wakaba 1.79 !!!cp ('t205');
4429 wakaba 1.116 !!!insert-element ('tr',, $token);
4430 wakaba 1.52 ## reprocess in the "in row" insertion mode
4431     }
4432 wakaba 1.79 } else {
4433     !!!cp ('t206');
4434 wakaba 1.52 }
4435    
4436     ## Clear back to table row context
4437     while (not {
4438     tr => 1, html => 1,
4439     }->{$self->{open_elements}->[-1]->[1]}) {
4440 wakaba 1.79 !!!cp ('t207');
4441 wakaba 1.52 pop @{$self->{open_elements}};
4442 wakaba 1.43 }
4443 wakaba 1.52
4444 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4445 wakaba 1.54 $self->{insertion_mode} = IN_CELL_IM;
4446 wakaba 1.52
4447     push @$active_formatting_elements, ['#marker', ''];
4448    
4449     !!!next-token;
4450     redo B;
4451     } elsif ({
4452     caption => 1, col => 1, colgroup => 1,
4453     tbody => 1, tfoot => 1, thead => 1,
4454 wakaba 1.54 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
4455 wakaba 1.52 }->{$token->{tag_name}}) {
4456 wakaba 1.54 if ($self->{insertion_mode} == IN_ROW_IM) {
4457 wakaba 1.52 ## As if </tr>
4458 wakaba 1.43 ## have an element in table scope
4459     my $i;
4460     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4461     my $node = $self->{open_elements}->[$_];
4462 wakaba 1.52 if ($node->[1] eq 'tr') {
4463 wakaba 1.79 !!!cp ('t208');
4464 wakaba 1.43 $i = $_;
4465     last INSCOPE;
4466     } elsif ({
4467 wakaba 1.83 html => 1,
4468    
4469     ## NOTE: This element does not appear here, maybe.
4470     table => 1,
4471 wakaba 1.43 }->{$node->[1]}) {
4472 wakaba 1.79 !!!cp ('t209');
4473 wakaba 1.43 last INSCOPE;
4474     }
4475     } # INSCOPE
4476 wakaba 1.79 unless (defined $i) {
4477     !!!cp ('t210');
4478 wakaba 1.83 ## TODO: This type is wrong.
4479 wakaba 1.113 !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);
4480 wakaba 1.52 ## Ignore the token
4481     !!!next-token;
4482 wakaba 1.43 redo B;
4483     }
4484    
4485 wakaba 1.52 ## Clear back to table row context
4486     while (not {
4487     tr => 1, html => 1,
4488     }->{$self->{open_elements}->[-1]->[1]}) {
4489 wakaba 1.79 !!!cp ('t211');
4490 wakaba 1.83 ## ISSUE: Can this case be reached?
4491 wakaba 1.52 pop @{$self->{open_elements}};
4492 wakaba 1.1 }
4493 wakaba 1.43
4494 wakaba 1.52 pop @{$self->{open_elements}}; # tr
4495 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
4496 wakaba 1.52 if ($token->{tag_name} eq 'tr') {
4497 wakaba 1.79 !!!cp ('t212');
4498 wakaba 1.52 ## reprocess
4499     redo B;
4500     } else {
4501 wakaba 1.79 !!!cp ('t213');
4502 wakaba 1.52 ## reprocess in the "in table body" insertion mode...
4503     }
4504 wakaba 1.1 }
4505 wakaba 1.52
4506 wakaba 1.54 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
4507 wakaba 1.52 ## have an element in table scope
4508 wakaba 1.43 my $i;
4509     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4510     my $node = $self->{open_elements}->[$_];
4511 wakaba 1.52 if ({
4512     tbody => 1, thead => 1, tfoot => 1,
4513     }->{$node->[1]}) {
4514 wakaba 1.79 !!!cp ('t214');
4515 wakaba 1.43 $i = $_;
4516     last INSCOPE;
4517     } elsif ({
4518     table => 1, html => 1,
4519     }->{$node->[1]}) {
4520 wakaba 1.79 !!!cp ('t215');
4521 wakaba 1.43 last INSCOPE;
4522     }
4523     } # INSCOPE
4524 wakaba 1.52 unless (defined $i) {
4525 wakaba 1.79 !!!cp ('t216');
4526 wakaba 1.82 ## TODO: This erorr type ios wrong.
4527 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4528 wakaba 1.52 ## Ignore the token
4529     !!!next-token;
4530 wakaba 1.43 redo B;
4531     }
4532 wakaba 1.52
4533     ## Clear back to table body context
4534     while (not {
4535     tbody => 1, tfoot => 1, thead => 1, html => 1,
4536     }->{$self->{open_elements}->[-1]->[1]}) {
4537 wakaba 1.79 !!!cp ('t217');
4538 wakaba 1.83 ## ISSUE: Can this state be reached?
4539 wakaba 1.52 pop @{$self->{open_elements}};
4540 wakaba 1.43 }
4541    
4542 wakaba 1.52 ## As if <{current node}>
4543     ## have an element in table scope
4544     ## true by definition
4545 wakaba 1.43
4546 wakaba 1.52 ## Clear back to table body context
4547     ## nop by definition
4548 wakaba 1.43
4549 wakaba 1.52 pop @{$self->{open_elements}};
4550 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
4551 wakaba 1.52 ## reprocess in "in table" insertion mode...
4552 wakaba 1.79 } else {
4553     !!!cp ('t218');
4554 wakaba 1.52 }
4555    
4556     if ($token->{tag_name} eq 'col') {
4557     ## Clear back to table context
4558     while ($self->{open_elements}->[-1]->[1] ne 'table' and
4559     $self->{open_elements}->[-1]->[1] ne 'html') {
4560 wakaba 1.79 !!!cp ('t219');
4561 wakaba 1.83 ## ISSUE: Can this state be reached?
4562 wakaba 1.52 pop @{$self->{open_elements}};
4563     }
4564 wakaba 1.43
4565 wakaba 1.116 !!!insert-element ('colgroup',, $token);
4566 wakaba 1.54 $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
4567 wakaba 1.52 ## reprocess
4568 wakaba 1.43 redo B;
4569 wakaba 1.52 } elsif ({
4570     caption => 1,
4571     colgroup => 1,
4572     tbody => 1, tfoot => 1, thead => 1,
4573     }->{$token->{tag_name}}) {
4574     ## Clear back to table context
4575     while ($self->{open_elements}->[-1]->[1] ne 'table' and
4576     $self->{open_elements}->[-1]->[1] ne 'html') {
4577 wakaba 1.79 !!!cp ('t220');
4578 wakaba 1.83 ## ISSUE: Can this state be reached?
4579 wakaba 1.52 pop @{$self->{open_elements}};
4580 wakaba 1.1 }
4581 wakaba 1.52
4582     push @$active_formatting_elements, ['#marker', '']
4583     if $token->{tag_name} eq 'caption';
4584    
4585 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4586 wakaba 1.52 $self->{insertion_mode} = {
4587 wakaba 1.54 caption => IN_CAPTION_IM,
4588     colgroup => IN_COLUMN_GROUP_IM,
4589     tbody => IN_TABLE_BODY_IM,
4590     tfoot => IN_TABLE_BODY_IM,
4591     thead => IN_TABLE_BODY_IM,
4592 wakaba 1.52 }->{$token->{tag_name}};
4593 wakaba 1.1 !!!next-token;
4594     redo B;
4595 wakaba 1.52 } else {
4596     die "$0: in table: <>: $token->{tag_name}";
4597 wakaba 1.1 }
4598 wakaba 1.52 } elsif ($token->{tag_name} eq 'table') {
4599 wakaba 1.122 !!!parse-error (type => 'not closed',
4600     value => $self->{open_elements}->[-1]->[0]
4601     ->manakai_local_name,
4602     token => $token);
4603 wakaba 1.1
4604 wakaba 1.52 ## As if </table>
4605 wakaba 1.1 ## have a table element in table scope
4606     my $i;
4607 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4608     my $node = $self->{open_elements}->[$_];
4609 wakaba 1.52 if ($node->[1] eq 'table') {
4610 wakaba 1.79 !!!cp ('t221');
4611 wakaba 1.1 $i = $_;
4612     last INSCOPE;
4613     } elsif ({
4614 wakaba 1.83 #table => 1,
4615     html => 1,
4616 wakaba 1.1 }->{$node->[1]}) {
4617 wakaba 1.79 !!!cp ('t222');
4618 wakaba 1.1 last INSCOPE;
4619     }
4620     } # INSCOPE
4621     unless (defined $i) {
4622 wakaba 1.79 !!!cp ('t223');
4623 wakaba 1.83 ## TODO: The following is wrong, maybe.
4624 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:table', token => $token);
4625 wakaba 1.52 ## Ignore tokens </table><table>
4626 wakaba 1.1 !!!next-token;
4627     redo B;
4628     }
4629    
4630 wakaba 1.106 ## TODO: Followings are removed from the latest spec.
4631 wakaba 1.1 ## generate implied end tags
4632 wakaba 1.86 while ({
4633     dd => 1, dt => 1, li => 1, p => 1,
4634     }->{$self->{open_elements}->[-1]->[1]}) {
4635 wakaba 1.79 !!!cp ('t224');
4636 wakaba 1.86 pop @{$self->{open_elements}};
4637 wakaba 1.1 }
4638    
4639 wakaba 1.52 if ($self->{open_elements}->[-1]->[1] ne 'table') {
4640 wakaba 1.79 !!!cp ('t225');
4641 wakaba 1.122 ## NOTE: |<table><tr><table>|
4642     !!!parse-error (type => 'not closed',
4643     value => $self->{open_elements}->[-1]->[0]
4644     ->manakai_local_name,
4645     token => $token);
4646 wakaba 1.79 } else {
4647     !!!cp ('t226');
4648 wakaba 1.1 }
4649    
4650 wakaba 1.3 splice @{$self->{open_elements}}, $i;
4651 wakaba 1.95 pop @{$open_tables};
4652 wakaba 1.1
4653 wakaba 1.52 $self->_reset_insertion_mode;
4654 wakaba 1.1
4655     ## reprocess
4656     redo B;
4657 wakaba 1.100 } elsif ($token->{tag_name} eq 'style') {
4658     if (not $open_tables->[-1]->[1]) { # tainted
4659     !!!cp ('t227.8');
4660     ## NOTE: This is a "as if in head" code clone.
4661     $parse_rcdata->(CDATA_CONTENT_MODEL);
4662     redo B;
4663     } else {
4664     !!!cp ('t227.7');
4665     #
4666     }
4667     } elsif ($token->{tag_name} eq 'script') {
4668     if (not $open_tables->[-1]->[1]) { # tainted
4669     !!!cp ('t227.6');
4670     ## NOTE: This is a "as if in head" code clone.
4671     $script_start_tag->();
4672     redo B;
4673     } else {
4674     !!!cp ('t227.5');
4675     #
4676     }
4677 wakaba 1.98 } elsif ($token->{tag_name} eq 'input') {
4678     if (not $open_tables->[-1]->[1]) { # tainted
4679     if ($token->{attributes}->{type}) { ## TODO: case
4680     my $type = lc $token->{attributes}->{type}->{value};
4681     if ($type eq 'hidden') {
4682     !!!cp ('t227.3');
4683 wakaba 1.113 !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);
4684 wakaba 1.98
4685 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4686 wakaba 1.98
4687     ## TODO: form element pointer
4688    
4689     pop @{$self->{open_elements}};
4690    
4691     !!!next-token;
4692     redo B;
4693     } else {
4694     !!!cp ('t227.2');
4695     #
4696     }
4697     } else {
4698     !!!cp ('t227.1');
4699     #
4700     }
4701     } else {
4702     !!!cp ('t227.4');
4703     #
4704     }
4705 wakaba 1.58 } else {
4706 wakaba 1.79 !!!cp ('t227');
4707 wakaba 1.58 #
4708     }
4709 wakaba 1.98
4710 wakaba 1.113 !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);
4711 wakaba 1.98
4712     $insert = $insert_to_foster;
4713     #
4714 wakaba 1.58 } elsif ($token->{type} == END_TAG_TOKEN) {
4715 wakaba 1.52 if ($token->{tag_name} eq 'tr' and
4716 wakaba 1.54 $self->{insertion_mode} == IN_ROW_IM) {
4717 wakaba 1.52 ## have an element in table scope
4718     my $i;
4719     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4720     my $node = $self->{open_elements}->[$_];
4721     if ($node->[1] eq $token->{tag_name}) {
4722 wakaba 1.79 !!!cp ('t228');
4723 wakaba 1.52 $i = $_;
4724     last INSCOPE;
4725     } elsif ({
4726     table => 1, html => 1,
4727     }->{$node->[1]}) {
4728 wakaba 1.79 !!!cp ('t229');
4729 wakaba 1.52 last INSCOPE;
4730     }
4731     } # INSCOPE
4732     unless (defined $i) {
4733 wakaba 1.79 !!!cp ('t230');
4734 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4735 wakaba 1.52 ## Ignore the token
4736 wakaba 1.42 !!!next-token;
4737     redo B;
4738 wakaba 1.79 } else {
4739     !!!cp ('t232');
4740 wakaba 1.42 }
4741    
4742 wakaba 1.52 ## Clear back to table row context
4743     while (not {
4744     tr => 1, html => 1,
4745     }->{$self->{open_elements}->[-1]->[1]}) {
4746 wakaba 1.79 !!!cp ('t231');
4747 wakaba 1.83 ## ISSUE: Can this state be reached?
4748 wakaba 1.52 pop @{$self->{open_elements}};
4749     }
4750 wakaba 1.42
4751 wakaba 1.52 pop @{$self->{open_elements}}; # tr
4752 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
4753 wakaba 1.52 !!!next-token;
4754     redo B;
4755     } elsif ($token->{tag_name} eq 'table') {
4756 wakaba 1.54 if ($self->{insertion_mode} == IN_ROW_IM) {
4757 wakaba 1.52 ## As if </tr>
4758     ## have an element in table scope
4759     my $i;
4760     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4761     my $node = $self->{open_elements}->[$_];
4762     if ($node->[1] eq 'tr') {
4763 wakaba 1.79 !!!cp ('t233');
4764 wakaba 1.52 $i = $_;
4765     last INSCOPE;
4766     } elsif ({
4767     table => 1, html => 1,
4768     }->{$node->[1]}) {
4769 wakaba 1.79 !!!cp ('t234');
4770 wakaba 1.52 last INSCOPE;
4771 wakaba 1.42 }
4772 wakaba 1.52 } # INSCOPE
4773     unless (defined $i) {
4774 wakaba 1.79 !!!cp ('t235');
4775 wakaba 1.83 ## TODO: The following is wrong.
4776 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);
4777 wakaba 1.52 ## Ignore the token
4778     !!!next-token;
4779     redo B;
4780 wakaba 1.42 }
4781 wakaba 1.52
4782     ## Clear back to table row context
4783     while (not {
4784     tr => 1, html => 1,
4785     }->{$self->{open_elements}->[-1]->[1]}) {
4786 wakaba 1.79 !!!cp ('t236');
4787 wakaba 1.83 ## ISSUE: Can this state be reached?
4788 wakaba 1.46 pop @{$self->{open_elements}};
4789 wakaba 1.1 }
4790 wakaba 1.46
4791 wakaba 1.52 pop @{$self->{open_elements}}; # tr
4792 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
4793 wakaba 1.46 ## reprocess in the "in table body" insertion mode...
4794 wakaba 1.1 }
4795    
4796 wakaba 1.54 if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
4797 wakaba 1.52 ## have an element in table scope
4798     my $i;
4799     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4800     my $node = $self->{open_elements}->[$_];
4801     if ({
4802     tbody => 1, thead => 1, tfoot => 1,
4803     }->{$node->[1]}) {
4804 wakaba 1.79 !!!cp ('t237');
4805 wakaba 1.52 $i = $_;
4806     last INSCOPE;
4807     } elsif ({
4808     table => 1, html => 1,
4809     }->{$node->[1]}) {
4810 wakaba 1.79 !!!cp ('t238');
4811 wakaba 1.52 last INSCOPE;
4812     }
4813     } # INSCOPE
4814     unless (defined $i) {
4815 wakaba 1.79 !!!cp ('t239');
4816 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4817 wakaba 1.52 ## Ignore the token
4818     !!!next-token;
4819     redo B;
4820 wakaba 1.47 }
4821    
4822     ## Clear back to table body context
4823     while (not {
4824     tbody => 1, tfoot => 1, thead => 1, html => 1,
4825     }->{$self->{open_elements}->[-1]->[1]}) {
4826 wakaba 1.79 !!!cp ('t240');
4827 wakaba 1.47 pop @{$self->{open_elements}};
4828     }
4829    
4830 wakaba 1.52 ## As if <{current node}>
4831     ## have an element in table scope
4832     ## true by definition
4833    
4834     ## Clear back to table body context
4835     ## nop by definition
4836    
4837     pop @{$self->{open_elements}};
4838 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
4839 wakaba 1.52 ## reprocess in the "in table" insertion mode...
4840     }
4841    
4842 wakaba 1.94 ## NOTE: </table> in the "in table" insertion mode.
4843     ## When you edit the code fragment below, please ensure that
4844     ## the code for <table> in the "in table" insertion mode
4845     ## is synced with it.
4846    
4847 wakaba 1.52 ## have a table element in table scope
4848     my $i;
4849     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4850     my $node = $self->{open_elements}->[$_];
4851     if ($node->[1] eq $token->{tag_name}) {
4852 wakaba 1.79 !!!cp ('t241');
4853 wakaba 1.52 $i = $_;
4854     last INSCOPE;
4855     } elsif ({
4856     table => 1, html => 1,
4857     }->{$node->[1]}) {
4858 wakaba 1.79 !!!cp ('t242');
4859 wakaba 1.52 last INSCOPE;
4860 wakaba 1.47 }
4861 wakaba 1.52 } # INSCOPE
4862     unless (defined $i) {
4863 wakaba 1.79 !!!cp ('t243');
4864 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4865 wakaba 1.52 ## Ignore the token
4866     !!!next-token;
4867     redo B;
4868 wakaba 1.3 }
4869 wakaba 1.52
4870     splice @{$self->{open_elements}}, $i;
4871 wakaba 1.95 pop @{$open_tables};
4872 wakaba 1.1
4873 wakaba 1.52 $self->_reset_insertion_mode;
4874 wakaba 1.47
4875     !!!next-token;
4876     redo B;
4877     } elsif ({
4878 wakaba 1.48 tbody => 1, tfoot => 1, thead => 1,
4879 wakaba 1.52 }->{$token->{tag_name}} and
4880 wakaba 1.56 $self->{insertion_mode} & ROW_IMS) {
4881 wakaba 1.54 if ($self->{insertion_mode} == IN_ROW_IM) {
4882 wakaba 1.52 ## have an element in table scope
4883     my $i;
4884     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4885     my $node = $self->{open_elements}->[$_];
4886     if ($node->[1] eq $token->{tag_name}) {
4887 wakaba 1.79 !!!cp ('t247');
4888 wakaba 1.52 $i = $_;
4889     last INSCOPE;
4890     } elsif ({
4891     table => 1, html => 1,
4892     }->{$node->[1]}) {
4893 wakaba 1.79 !!!cp ('t248');
4894 wakaba 1.52 last INSCOPE;
4895     }
4896     } # INSCOPE
4897     unless (defined $i) {
4898 wakaba 1.79 !!!cp ('t249');
4899 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4900 wakaba 1.52 ## Ignore the token
4901     !!!next-token;
4902     redo B;
4903     }
4904    
4905 wakaba 1.48 ## As if </tr>
4906     ## have an element in table scope
4907     my $i;
4908     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4909     my $node = $self->{open_elements}->[$_];
4910     if ($node->[1] eq 'tr') {
4911 wakaba 1.79 !!!cp ('t250');
4912 wakaba 1.48 $i = $_;
4913     last INSCOPE;
4914     } elsif ({
4915     table => 1, html => 1,
4916     }->{$node->[1]}) {
4917 wakaba 1.79 !!!cp ('t251');
4918 wakaba 1.48 last INSCOPE;
4919     }
4920     } # INSCOPE
4921 wakaba 1.52 unless (defined $i) {
4922 wakaba 1.79 !!!cp ('t252');
4923 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:tr', token => $token);
4924 wakaba 1.52 ## Ignore the token
4925     !!!next-token;
4926     redo B;
4927     }
4928 wakaba 1.48
4929     ## Clear back to table row context
4930     while (not {
4931     tr => 1, html => 1,
4932     }->{$self->{open_elements}->[-1]->[1]}) {
4933 wakaba 1.79 !!!cp ('t253');
4934 wakaba 1.83 ## ISSUE: Can this case be reached?
4935 wakaba 1.48 pop @{$self->{open_elements}};
4936     }
4937    
4938     pop @{$self->{open_elements}}; # tr
4939 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_BODY_IM;
4940 wakaba 1.52 ## reprocess in the "in table body" insertion mode...
4941     }
4942    
4943     ## have an element in table scope
4944     my $i;
4945     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4946     my $node = $self->{open_elements}->[$_];
4947     if ($node->[1] eq $token->{tag_name}) {
4948 wakaba 1.79 !!!cp ('t254');
4949 wakaba 1.52 $i = $_;
4950     last INSCOPE;
4951     } elsif ({
4952     table => 1, html => 1,
4953     }->{$node->[1]}) {
4954 wakaba 1.79 !!!cp ('t255');
4955 wakaba 1.52 last INSCOPE;
4956     }
4957     } # INSCOPE
4958     unless (defined $i) {
4959 wakaba 1.79 !!!cp ('t256');
4960 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4961 wakaba 1.52 ## Ignore the token
4962     !!!next-token;
4963     redo B;
4964     }
4965    
4966     ## Clear back to table body context
4967     while (not {
4968     tbody => 1, tfoot => 1, thead => 1, html => 1,
4969     }->{$self->{open_elements}->[-1]->[1]}) {
4970 wakaba 1.79 !!!cp ('t257');
4971 wakaba 1.83 ## ISSUE: Can this case be reached?
4972 wakaba 1.52 pop @{$self->{open_elements}};
4973     }
4974    
4975     pop @{$self->{open_elements}};
4976 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
4977 wakaba 1.52 !!!next-token;
4978     redo B;
4979     } elsif ({
4980     body => 1, caption => 1, col => 1, colgroup => 1,
4981     html => 1, td => 1, th => 1,
4982 wakaba 1.54 tr => 1, # $self->{insertion_mode} == IN_ROW_IM
4983     tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
4984 wakaba 1.52 }->{$token->{tag_name}}) {
4985 wakaba 1.79 !!!cp ('t258');
4986 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4987 wakaba 1.52 ## Ignore the token
4988     !!!next-token;
4989     redo B;
4990 wakaba 1.58 } else {
4991 wakaba 1.79 !!!cp ('t259');
4992 wakaba 1.113 !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);
4993 wakaba 1.52
4994 wakaba 1.58 $insert = $insert_to_foster;
4995     #
4996     }
4997 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4998     unless ($self->{open_elements}->[-1]->[1] eq 'html' and
4999     @{$self->{open_elements}} == 1) { # redundant, maybe
5000 wakaba 1.113 !!!parse-error (type => 'in body:#eof', token => $token);
5001 wakaba 1.104 !!!cp ('t259.1');
5002 wakaba 1.105 #
5003 wakaba 1.104 } else {
5004     !!!cp ('t259.2');
5005 wakaba 1.105 #
5006 wakaba 1.104 }
5007    
5008     ## Stop parsing
5009     last B;
5010 wakaba 1.58 } else {
5011     die "$0: $token->{type}: Unknown token type";
5012     }
5013 wakaba 1.54 } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
5014 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
5015 wakaba 1.52 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5016     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5017     unless (length $token->{data}) {
5018 wakaba 1.79 !!!cp ('t260');
5019 wakaba 1.52 !!!next-token;
5020     redo B;
5021     }
5022     }
5023    
5024 wakaba 1.79 !!!cp ('t261');
5025 wakaba 1.52 #
5026 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
5027 wakaba 1.52 if ($token->{tag_name} eq 'col') {
5028 wakaba 1.79 !!!cp ('t262');
5029 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5030 wakaba 1.52 pop @{$self->{open_elements}};
5031     !!!next-token;
5032     redo B;
5033     } else {
5034 wakaba 1.79 !!!cp ('t263');
5035 wakaba 1.52 #
5036     }
5037 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
5038 wakaba 1.52 if ($token->{tag_name} eq 'colgroup') {
5039     if ($self->{open_elements}->[-1]->[1] eq 'html') {
5040 wakaba 1.79 !!!cp ('t264');
5041 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);
5042 wakaba 1.52 ## Ignore the token
5043     !!!next-token;
5044     redo B;
5045     } else {
5046 wakaba 1.79 !!!cp ('t265');
5047 wakaba 1.52 pop @{$self->{open_elements}}; # colgroup
5048 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
5049 wakaba 1.52 !!!next-token;
5050     redo B;
5051     }
5052     } elsif ($token->{tag_name} eq 'col') {
5053 wakaba 1.79 !!!cp ('t266');
5054 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:col', token => $token);
5055 wakaba 1.52 ## Ignore the token
5056     !!!next-token;
5057     redo B;
5058     } else {
5059 wakaba 1.79 !!!cp ('t267');
5060 wakaba 1.52 #
5061     }
5062 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5063     if ($self->{open_elements}->[-1]->[1] eq 'html' or
5064     @{$self->{open_elements}} == 1) { # redundant, maybe
5065     !!!cp ('t270.2');
5066     ## Stop parsing.
5067     last B;
5068     } else {
5069     ## NOTE: As if </colgroup>.
5070     !!!cp ('t270.1');
5071     pop @{$self->{open_elements}}; # colgroup
5072     $self->{insertion_mode} = IN_TABLE_IM;
5073     ## Reprocess.
5074     redo B;
5075     }
5076     } else {
5077     die "$0: $token->{type}: Unknown token type";
5078     }
5079 wakaba 1.52
5080     ## As if </colgroup>
5081     if ($self->{open_elements}->[-1]->[1] eq 'html') {
5082 wakaba 1.79 !!!cp ('t269');
5083 wakaba 1.104 ## TODO: Wrong error type?
5084 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);
5085 wakaba 1.52 ## Ignore the token
5086     !!!next-token;
5087     redo B;
5088     } else {
5089 wakaba 1.79 !!!cp ('t270');
5090 wakaba 1.52 pop @{$self->{open_elements}}; # colgroup
5091 wakaba 1.54 $self->{insertion_mode} = IN_TABLE_IM;
5092 wakaba 1.52 ## reprocess
5093     redo B;
5094     }
5095 wakaba 1.101 } elsif ($self->{insertion_mode} & SELECT_IMS) {
5096 wakaba 1.58 if ($token->{type} == CHARACTER_TOKEN) {
5097 wakaba 1.79 !!!cp ('t271');
5098 wakaba 1.58 $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5099     !!!next-token;
5100     redo B;
5101     } elsif ($token->{type} == START_TAG_TOKEN) {
5102 wakaba 1.52 if ($token->{tag_name} eq 'option') {
5103     if ($self->{open_elements}->[-1]->[1] eq 'option') {
5104 wakaba 1.79 !!!cp ('t272');
5105 wakaba 1.52 ## As if </option>
5106     pop @{$self->{open_elements}};
5107 wakaba 1.79 } else {
5108     !!!cp ('t273');
5109 wakaba 1.52 }
5110    
5111 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5112 wakaba 1.52 !!!next-token;
5113     redo B;
5114     } elsif ($token->{tag_name} eq 'optgroup') {
5115     if ($self->{open_elements}->[-1]->[1] eq 'option') {
5116 wakaba 1.79 !!!cp ('t274');
5117 wakaba 1.52 ## As if </option>
5118     pop @{$self->{open_elements}};
5119 wakaba 1.79 } else {
5120     !!!cp ('t275');
5121 wakaba 1.52 }
5122    
5123     if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {
5124 wakaba 1.79 !!!cp ('t276');
5125 wakaba 1.52 ## As if </optgroup>
5126     pop @{$self->{open_elements}};
5127 wakaba 1.79 } else {
5128     !!!cp ('t277');
5129 wakaba 1.52 }
5130    
5131 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5132 wakaba 1.52 !!!next-token;
5133     redo B;
5134 wakaba 1.101 } elsif ($token->{tag_name} eq 'select' or
5135     $token->{tag_name} eq 'input' or
5136     ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
5137     {
5138     caption => 1, table => 1,
5139     tbody => 1, tfoot => 1, thead => 1,
5140     tr => 1, td => 1, th => 1,
5141     }->{$token->{tag_name}})) {
5142     ## TODO: The type below is not good - <select> is replaced by </select>
5143 wakaba 1.113 !!!parse-error (type => 'not closed:select', token => $token);
5144 wakaba 1.101 ## NOTE: As if the token were </select> (<select> case) or
5145     ## as if there were </select> (otherwise).
5146 wakaba 1.52 ## have an element in table scope
5147     my $i;
5148     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5149     my $node = $self->{open_elements}->[$_];
5150 wakaba 1.101 if ($node->[1] eq 'select') {
5151 wakaba 1.79 !!!cp ('t278');
5152 wakaba 1.52 $i = $_;
5153     last INSCOPE;
5154     } elsif ({
5155     table => 1, html => 1,
5156     }->{$node->[1]}) {
5157 wakaba 1.79 !!!cp ('t279');
5158 wakaba 1.52 last INSCOPE;
5159 wakaba 1.47 }
5160 wakaba 1.52 } # INSCOPE
5161     unless (defined $i) {
5162 wakaba 1.79 !!!cp ('t280');
5163 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:select', token => $token);
5164 wakaba 1.52 ## Ignore the token
5165     !!!next-token;
5166     redo B;
5167 wakaba 1.47 }
5168 wakaba 1.52
5169 wakaba 1.79 !!!cp ('t281');
5170 wakaba 1.52 splice @{$self->{open_elements}}, $i;
5171    
5172     $self->_reset_insertion_mode;
5173 wakaba 1.47
5174 wakaba 1.101 if ($token->{tag_name} eq 'select') {
5175     !!!cp ('t281.2');
5176     !!!next-token;
5177     redo B;
5178     } else {
5179     !!!cp ('t281.1');
5180     ## Reprocess the token.
5181     redo B;
5182     }
5183 wakaba 1.58 } else {
5184 wakaba 1.79 !!!cp ('t282');
5185 wakaba 1.113 !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);
5186 wakaba 1.58 ## Ignore the token
5187     !!!next-token;
5188     redo B;
5189     }
5190     } elsif ($token->{type} == END_TAG_TOKEN) {
5191 wakaba 1.52 if ($token->{tag_name} eq 'optgroup') {
5192     if ($self->{open_elements}->[-1]->[1] eq 'option' and
5193     $self->{open_elements}->[-2]->[1] eq 'optgroup') {
5194 wakaba 1.79 !!!cp ('t283');
5195 wakaba 1.52 ## As if </option>
5196     splice @{$self->{open_elements}}, -2;
5197     } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {
5198 wakaba 1.79 !!!cp ('t284');
5199 wakaba 1.52 pop @{$self->{open_elements}};
5200     } else {
5201 wakaba 1.79 !!!cp ('t285');
5202 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5203 wakaba 1.52 ## Ignore the token
5204     }
5205     !!!next-token;
5206     redo B;
5207     } elsif ($token->{tag_name} eq 'option') {
5208     if ($self->{open_elements}->[-1]->[1] eq 'option') {
5209 wakaba 1.79 !!!cp ('t286');
5210 wakaba 1.47 pop @{$self->{open_elements}};
5211 wakaba 1.52 } else {
5212 wakaba 1.79 !!!cp ('t287');
5213 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5214 wakaba 1.52 ## Ignore the token
5215 wakaba 1.1 }
5216 wakaba 1.52 !!!next-token;
5217     redo B;
5218     } elsif ($token->{tag_name} eq 'select') {
5219     ## have an element in table scope
5220     my $i;
5221     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5222     my $node = $self->{open_elements}->[$_];
5223     if ($node->[1] eq $token->{tag_name}) {
5224 wakaba 1.79 !!!cp ('t288');
5225 wakaba 1.52 $i = $_;
5226     last INSCOPE;
5227     } elsif ({
5228     table => 1, html => 1,
5229     }->{$node->[1]}) {
5230 wakaba 1.79 !!!cp ('t289');
5231 wakaba 1.52 last INSCOPE;
5232 wakaba 1.48 }
5233 wakaba 1.52 } # INSCOPE
5234     unless (defined $i) {
5235 wakaba 1.79 !!!cp ('t290');
5236 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5237 wakaba 1.52 ## Ignore the token
5238     !!!next-token;
5239 wakaba 1.48 redo B;
5240 wakaba 1.52 }
5241    
5242 wakaba 1.79 !!!cp ('t291');
5243 wakaba 1.52 splice @{$self->{open_elements}}, $i;
5244    
5245     $self->_reset_insertion_mode;
5246    
5247     !!!next-token;
5248     redo B;
5249 wakaba 1.101 } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
5250     {
5251     caption => 1, table => 1, tbody => 1,
5252     tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
5253     }->{$token->{tag_name}}) {
5254 wakaba 1.83 ## TODO: The following is wrong?
5255 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5256 wakaba 1.52
5257     ## have an element in table scope
5258     my $i;
5259     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5260     my $node = $self->{open_elements}->[$_];
5261     if ($node->[1] eq $token->{tag_name}) {
5262 wakaba 1.79 !!!cp ('t292');
5263 wakaba 1.52 $i = $_;
5264     last INSCOPE;
5265     } elsif ({
5266     table => 1, html => 1,
5267     }->{$node->[1]}) {
5268 wakaba 1.79 !!!cp ('t293');
5269 wakaba 1.52 last INSCOPE;
5270 wakaba 1.1 }
5271 wakaba 1.52 } # INSCOPE
5272     unless (defined $i) {
5273 wakaba 1.79 !!!cp ('t294');
5274 wakaba 1.52 ## Ignore the token
5275 wakaba 1.1 !!!next-token;
5276     redo B;
5277     }
5278 wakaba 1.52
5279     ## As if </select>
5280     ## have an element in table scope
5281     undef $i;
5282 wakaba 1.3 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5283     my $node = $self->{open_elements}->[$_];
5284 wakaba 1.52 if ($node->[1] eq 'select') {
5285 wakaba 1.79 !!!cp ('t295');
5286 wakaba 1.1 $i = $_;
5287     last INSCOPE;
5288     } elsif ({
5289     table => 1, html => 1,
5290 wakaba 1.52 }->{$node->[1]}) {
5291 wakaba 1.83 ## ISSUE: Can this state be reached?
5292 wakaba 1.79 !!!cp ('t296');
5293 wakaba 1.52 last INSCOPE;
5294     }
5295     } # INSCOPE
5296     unless (defined $i) {
5297 wakaba 1.79 !!!cp ('t297');
5298 wakaba 1.83 ## TODO: The following error type is correct?
5299 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:select', token => $token);
5300 wakaba 1.52 ## Ignore the </select> token
5301     !!!next-token; ## TODO: ok?
5302     redo B;
5303     }
5304    
5305 wakaba 1.79 !!!cp ('t298');
5306 wakaba 1.52 splice @{$self->{open_elements}}, $i;
5307    
5308     $self->_reset_insertion_mode;
5309    
5310     ## reprocess
5311     redo B;
5312 wakaba 1.58 } else {
5313 wakaba 1.79 !!!cp ('t299');
5314 wakaba 1.113 !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);
5315 wakaba 1.52 ## Ignore the token
5316     !!!next-token;
5317     redo B;
5318 wakaba 1.58 }
5319 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5320     unless ($self->{open_elements}->[-1]->[1] eq 'html' and
5321     @{$self->{open_elements}} == 1) { # redundant, maybe
5322     !!!cp ('t299.1');
5323 wakaba 1.113 !!!parse-error (type => 'in body:#eof', token => $token);
5324 wakaba 1.104 } else {
5325     !!!cp ('t299.2');
5326     }
5327    
5328     ## Stop parsing.
5329     last B;
5330 wakaba 1.58 } else {
5331     die "$0: $token->{type}: Unknown token type";
5332     }
5333 wakaba 1.56 } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
5334 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
5335 wakaba 1.52 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5336     my $data = $1;
5337     ## As if in body
5338     $reconstruct_active_formatting_elements->($insert_to_current);
5339    
5340     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5341    
5342     unless (length $token->{data}) {
5343 wakaba 1.79 !!!cp ('t300');
5344 wakaba 1.52 !!!next-token;
5345     redo B;
5346     }
5347     }
5348    
5349 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5350 wakaba 1.79 !!!cp ('t301');
5351 wakaba 1.113 !!!parse-error (type => 'after html:#character', token => $token);
5352 wakaba 1.52
5353 wakaba 1.84 ## Reprocess in the "after body" insertion mode.
5354 wakaba 1.79 } else {
5355     !!!cp ('t302');
5356 wakaba 1.52 }
5357    
5358     ## "after body" insertion mode
5359 wakaba 1.113 !!!parse-error (type => 'after body:#character', token => $token);
5360 wakaba 1.52
5361 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
5362 wakaba 1.52 ## reprocess
5363     redo B;
5364 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
5365 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5366 wakaba 1.79 !!!cp ('t303');
5367 wakaba 1.113 !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);
5368 wakaba 1.52
5369 wakaba 1.84 ## Reprocess in the "after body" insertion mode.
5370 wakaba 1.79 } else {
5371     !!!cp ('t304');
5372 wakaba 1.52 }
5373    
5374     ## "after body" insertion mode
5375 wakaba 1.113 !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);
5376 wakaba 1.52
5377 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
5378 wakaba 1.52 ## reprocess
5379     redo B;
5380 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
5381 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5382 wakaba 1.79 !!!cp ('t305');
5383 wakaba 1.113 !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);
5384 wakaba 1.52
5385 wakaba 1.54 $self->{insertion_mode} = AFTER_BODY_IM;
5386 wakaba 1.84 ## Reprocess in the "after body" insertion mode.
5387 wakaba 1.79 } else {
5388     !!!cp ('t306');
5389 wakaba 1.52 }
5390    
5391     ## "after body" insertion mode
5392     if ($token->{tag_name} eq 'html') {
5393     if (defined $self->{inner_html_node}) {
5394 wakaba 1.79 !!!cp ('t307');
5395 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:html', token => $token);
5396 wakaba 1.52 ## Ignore the token
5397     !!!next-token;
5398     redo B;
5399     } else {
5400 wakaba 1.79 !!!cp ('t308');
5401 wakaba 1.54 $self->{insertion_mode} = AFTER_HTML_BODY_IM;
5402 wakaba 1.52 !!!next-token;
5403     redo B;
5404     }
5405     } else {
5406 wakaba 1.79 !!!cp ('t309');
5407 wakaba 1.113 !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);
5408 wakaba 1.52
5409 wakaba 1.54 $self->{insertion_mode} = IN_BODY_IM;
5410 wakaba 1.52 ## reprocess
5411     redo B;
5412     }
5413 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5414     !!!cp ('t309.2');
5415     ## Stop parsing
5416     last B;
5417 wakaba 1.52 } else {
5418     die "$0: $token->{type}: Unknown token type";
5419     }
5420 wakaba 1.56 } elsif ($self->{insertion_mode} & FRAME_IMS) {
5421 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
5422 wakaba 1.52 if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5423     $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5424    
5425     unless (length $token->{data}) {
5426 wakaba 1.79 !!!cp ('t310');
5427 wakaba 1.52 !!!next-token;
5428     redo B;
5429     }
5430     }
5431    
5432     if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
5433 wakaba 1.54 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5434 wakaba 1.79 !!!cp ('t311');
5435 wakaba 1.113 !!!parse-error (type => 'in frameset:#character', token => $token);
5436 wakaba 1.54 } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
5437 wakaba 1.79 !!!cp ('t312');
5438 wakaba 1.113 !!!parse-error (type => 'after frameset:#character', token => $token);
5439 wakaba 1.52 } else { # "after html frameset"
5440 wakaba 1.79 !!!cp ('t313');
5441 wakaba 1.113 !!!parse-error (type => 'after html:#character', token => $token);
5442 wakaba 1.52
5443 wakaba 1.54 $self->{insertion_mode} = AFTER_FRAMESET_IM;
5444 wakaba 1.84 ## Reprocess in the "after frameset" insertion mode.
5445 wakaba 1.113 !!!parse-error (type => 'after frameset:#character', token => $token);
5446 wakaba 1.52 }
5447    
5448     ## Ignore the token.
5449     if (length $token->{data}) {
5450 wakaba 1.79 !!!cp ('t314');
5451 wakaba 1.52 ## reprocess the rest of characters
5452     } else {
5453 wakaba 1.79 !!!cp ('t315');
5454 wakaba 1.52 !!!next-token;
5455     }
5456     redo B;
5457     }
5458    
5459     die qq[$0: Character "$token->{data}"];
5460 wakaba 1.55 } elsif ($token->{type} == START_TAG_TOKEN) {
5461 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5462 wakaba 1.79 !!!cp ('t316');
5463 wakaba 1.113 !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);
5464 wakaba 1.1
5465 wakaba 1.54 $self->{insertion_mode} = AFTER_FRAMESET_IM;
5466 wakaba 1.84 ## Process in the "after frameset" insertion mode.
5467 wakaba 1.79 } else {
5468     !!!cp ('t317');
5469     }
5470 wakaba 1.1
5471 wakaba 1.52 if ($token->{tag_name} eq 'frameset' and
5472 wakaba 1.54 $self->{insertion_mode} == IN_FRAMESET_IM) {
5473 wakaba 1.79 !!!cp ('t318');
5474 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5475 wakaba 1.52 !!!next-token;
5476     redo B;
5477     } elsif ($token->{tag_name} eq 'frame' and
5478 wakaba 1.54 $self->{insertion_mode} == IN_FRAMESET_IM) {
5479 wakaba 1.79 !!!cp ('t319');
5480 wakaba 1.116 !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5481 wakaba 1.52 pop @{$self->{open_elements}};
5482     !!!next-token;
5483     redo B;
5484     } elsif ($token->{tag_name} eq 'noframes') {
5485 wakaba 1.79 !!!cp ('t320');
5486 wakaba 1.52 ## NOTE: As if in body.
5487 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
5488 wakaba 1.52 redo B;
5489     } else {
5490 wakaba 1.54 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5491 wakaba 1.79 !!!cp ('t321');
5492 wakaba 1.113 !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);
5493 wakaba 1.52 } else {
5494 wakaba 1.79 !!!cp ('t322');
5495 wakaba 1.113 !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);
5496 wakaba 1.52 }
5497     ## Ignore the token
5498     !!!next-token;
5499     redo B;
5500     }
5501 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
5502 wakaba 1.54 if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5503 wakaba 1.79 !!!cp ('t323');
5504 wakaba 1.113 !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);
5505 wakaba 1.1
5506 wakaba 1.54 $self->{insertion_mode} = AFTER_FRAMESET_IM;
5507 wakaba 1.84 ## Process in the "after frameset" insertion mode.
5508 wakaba 1.79 } else {
5509     !!!cp ('t324');
5510 wakaba 1.52 }
5511 wakaba 1.1
5512 wakaba 1.52 if ($token->{tag_name} eq 'frameset' and
5513 wakaba 1.54 $self->{insertion_mode} == IN_FRAMESET_IM) {
5514 wakaba 1.52 if ($self->{open_elements}->[-1]->[1] eq 'html' and
5515     @{$self->{open_elements}} == 1) {
5516 wakaba 1.79 !!!cp ('t325');
5517 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5518 wakaba 1.52 ## Ignore the token
5519     !!!next-token;
5520     } else {
5521 wakaba 1.79 !!!cp ('t326');
5522 wakaba 1.52 pop @{$self->{open_elements}};
5523     !!!next-token;
5524     }
5525 wakaba 1.47
5526 wakaba 1.52 if (not defined $self->{inner_html_node} and
5527     $self->{open_elements}->[-1]->[1] ne 'frameset') {
5528 wakaba 1.79 !!!cp ('t327');
5529 wakaba 1.54 $self->{insertion_mode} = AFTER_FRAMESET_IM;
5530 wakaba 1.79 } else {
5531     !!!cp ('t328');
5532 wakaba 1.52 }
5533     redo B;
5534     } elsif ($token->{tag_name} eq 'html' and
5535 wakaba 1.54 $self->{insertion_mode} == AFTER_FRAMESET_IM) {
5536 wakaba 1.79 !!!cp ('t329');
5537 wakaba 1.54 $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
5538 wakaba 1.52 !!!next-token;
5539     redo B;
5540     } else {
5541 wakaba 1.54 if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5542 wakaba 1.79 !!!cp ('t330');
5543 wakaba 1.113 !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);
5544 wakaba 1.52 } else {
5545 wakaba 1.79 !!!cp ('t331');
5546 wakaba 1.113 !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);
5547 wakaba 1.52 }
5548     ## Ignore the token
5549     !!!next-token;
5550     redo B;
5551     }
5552 wakaba 1.104 } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5553     unless ($self->{open_elements}->[-1]->[1] eq 'html' and
5554     @{$self->{open_elements}} == 1) { # redundant, maybe
5555     !!!cp ('t331.1');
5556 wakaba 1.113 !!!parse-error (type => 'in body:#eof', token => $token);
5557 wakaba 1.104 } else {
5558     !!!cp ('t331.2');
5559     }
5560    
5561     ## Stop parsing
5562     last B;
5563 wakaba 1.52 } else {
5564     die "$0: $token->{type}: Unknown token type";
5565     }
5566 wakaba 1.47
5567 wakaba 1.52 ## ISSUE: An issue in spec here
5568     } else {
5569     die "$0: $self->{insertion_mode}: Unknown insertion mode";
5570     }
5571 wakaba 1.47
5572 wakaba 1.52 ## "in body" insertion mode
5573 wakaba 1.55 if ($token->{type} == START_TAG_TOKEN) {
5574 wakaba 1.52 if ($token->{tag_name} eq 'script') {
5575 wakaba 1.79 !!!cp ('t332');
5576 wakaba 1.52 ## NOTE: This is an "as if in head" code clone
5577 wakaba 1.100 $script_start_tag->();
5578 wakaba 1.53 redo B;
5579 wakaba 1.52 } elsif ($token->{tag_name} eq 'style') {
5580 wakaba 1.79 !!!cp ('t333');
5581 wakaba 1.52 ## NOTE: This is an "as if in head" code clone
5582 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
5583 wakaba 1.53 redo B;
5584 wakaba 1.52 } elsif ({
5585     base => 1, link => 1,
5586     }->{$token->{tag_name}}) {
5587 wakaba 1.79 !!!cp ('t334');
5588 wakaba 1.52 ## NOTE: This is an "as if in head" code clone, only "-t" differs
5589 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5590 wakaba 1.52 pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5591     !!!next-token;
5592 wakaba 1.53 redo B;
5593 wakaba 1.52 } elsif ($token->{tag_name} eq 'meta') {
5594     ## NOTE: This is an "as if in head" code clone, only "-t" differs
5595 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5596 wakaba 1.66 my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5597 wakaba 1.46
5598 wakaba 1.52 unless ($self->{confident}) {
5599     if ($token->{attributes}->{charset}) { ## TODO: And if supported
5600 wakaba 1.79 !!!cp ('t335');
5601 wakaba 1.63 $self->{change_encoding}
5602 wakaba 1.114 ->($self, $token->{attributes}->{charset}->{value}, $token);
5603 wakaba 1.66
5604     $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5605     ->set_user_data (manakai_has_reference =>
5606     $token->{attributes}->{charset}
5607     ->{has_reference});
5608 wakaba 1.63 } elsif ($token->{attributes}->{content}) {
5609 wakaba 1.52 ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.
5610 wakaba 1.63 if ($token->{attributes}->{content}->{value}
5611 wakaba 1.70 =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
5612     [\x09-\x0D\x20]*=
5613 wakaba 1.52 [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
5614     ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
5615 wakaba 1.79 !!!cp ('t336');
5616 wakaba 1.63 $self->{change_encoding}
5617 wakaba 1.114 ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
5618 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5619     ->set_user_data (manakai_has_reference =>
5620     $token->{attributes}->{content}
5621     ->{has_reference});
5622 wakaba 1.63 }
5623 wakaba 1.52 }
5624 wakaba 1.66 } else {
5625     if ($token->{attributes}->{charset}) {
5626 wakaba 1.79 !!!cp ('t337');
5627 wakaba 1.66 $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5628     ->set_user_data (manakai_has_reference =>
5629     $token->{attributes}->{charset}
5630     ->{has_reference});
5631     }
5632 wakaba 1.68 if ($token->{attributes}->{content}) {
5633 wakaba 1.79 !!!cp ('t338');
5634 wakaba 1.68 $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5635     ->set_user_data (manakai_has_reference =>
5636     $token->{attributes}->{content}
5637     ->{has_reference});
5638     }
5639 wakaba 1.52 }
5640 wakaba 1.1
5641 wakaba 1.52 !!!next-token;
5642 wakaba 1.53 redo B;
5643 wakaba 1.52 } elsif ($token->{tag_name} eq 'title') {
5644 wakaba 1.79 !!!cp ('t341');
5645 wakaba 1.52 ## NOTE: This is an "as if in head" code clone
5646 wakaba 1.96 $parse_rcdata->(RCDATA_CONTENT_MODEL);
5647 wakaba 1.53 redo B;
5648 wakaba 1.52 } elsif ($token->{tag_name} eq 'body') {
5649 wakaba 1.113 !!!parse-error (type => 'in body:body', token => $token);
5650 wakaba 1.46
5651 wakaba 1.52 if (@{$self->{open_elements}} == 1 or
5652     $self->{open_elements}->[1]->[1] ne 'body') {
5653 wakaba 1.79 !!!cp ('t342');
5654 wakaba 1.52 ## Ignore the token
5655     } else {
5656     my $body_el = $self->{open_elements}->[1]->[0];
5657     for my $attr_name (keys %{$token->{attributes}}) {
5658     unless ($body_el->has_attribute_ns (undef, $attr_name)) {
5659 wakaba 1.79 !!!cp ('t343');
5660 wakaba 1.52 $body_el->set_attribute_ns
5661     (undef, [undef, $attr_name],
5662     $token->{attributes}->{$attr_name}->{value});
5663     }
5664     }
5665     }
5666     !!!next-token;
5667 wakaba 1.53 redo B;
5668 wakaba 1.52 } elsif ({
5669     address => 1, blockquote => 1, center => 1, dir => 1,
5670 wakaba 1.85 div => 1, dl => 1, fieldset => 1,
5671     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
5672 wakaba 1.97 menu => 1, ol => 1, p => 1, ul => 1,
5673     pre => 1, listing => 1,
5674 wakaba 1.109 form => 1,
5675     table => 1,
5676     hr => 1,
5677 wakaba 1.52 }->{$token->{tag_name}}) {
5678 wakaba 1.109 if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
5679     !!!cp ('t350');
5680 wakaba 1.113 !!!parse-error (type => 'in form:form', token => $token);
5681 wakaba 1.109 ## Ignore the token
5682     !!!next-token;
5683     redo B;
5684     }
5685    
5686 wakaba 1.52 ## has a p element in scope
5687     INSCOPE: for (reverse @{$self->{open_elements}}) {
5688     if ($_->[1] eq 'p') {
5689 wakaba 1.79 !!!cp ('t344');
5690 wakaba 1.52 !!!back-token;
5691 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'p',
5692     line => $token->{line}, column => $token->{column}};
5693 wakaba 1.53 redo B;
5694 wakaba 1.52 } elsif ({
5695 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
5696 wakaba 1.52 button => 1, marquee => 1, object => 1, html => 1,
5697     }->{$_->[1]}) {
5698 wakaba 1.79 !!!cp ('t345');
5699 wakaba 1.52 last INSCOPE;
5700     }
5701     } # INSCOPE
5702    
5703 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5704 wakaba 1.97 if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
5705 wakaba 1.52 !!!next-token;
5706 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
5707 wakaba 1.52 $token->{data} =~ s/^\x0A//;
5708     unless (length $token->{data}) {
5709 wakaba 1.79 !!!cp ('t346');
5710 wakaba 1.1 !!!next-token;
5711 wakaba 1.79 } else {
5712     !!!cp ('t349');
5713 wakaba 1.52 }
5714 wakaba 1.79 } else {
5715     !!!cp ('t348');
5716 wakaba 1.52 }
5717 wakaba 1.109 } elsif ($token->{tag_name} eq 'form') {
5718     !!!cp ('t347.1');
5719     $self->{form_element} = $self->{open_elements}->[-1]->[0];
5720    
5721     !!!next-token;
5722     } elsif ($token->{tag_name} eq 'table') {
5723     !!!cp ('t382');
5724     push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
5725    
5726     $self->{insertion_mode} = IN_TABLE_IM;
5727    
5728     !!!next-token;
5729     } elsif ($token->{tag_name} eq 'hr') {
5730     !!!cp ('t386');
5731     pop @{$self->{open_elements}};
5732    
5733     !!!next-token;
5734 wakaba 1.52 } else {
5735 wakaba 1.79 !!!cp ('t347');
5736 wakaba 1.52 !!!next-token;
5737     }
5738 wakaba 1.53 redo B;
5739 wakaba 1.109 } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
5740 wakaba 1.52 ## has a p element in scope
5741     INSCOPE: for (reverse @{$self->{open_elements}}) {
5742     if ($_->[1] eq 'p') {
5743 wakaba 1.79 !!!cp ('t353');
5744 wakaba 1.52 !!!back-token;
5745 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'p',
5746     line => $token->{line}, column => $token->{column}};
5747 wakaba 1.53 redo B;
5748 wakaba 1.52 } elsif ({
5749 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
5750 wakaba 1.52 button => 1, marquee => 1, object => 1, html => 1,
5751     }->{$_->[1]}) {
5752 wakaba 1.79 !!!cp ('t354');
5753 wakaba 1.52 last INSCOPE;
5754     }
5755     } # INSCOPE
5756    
5757     ## Step 1
5758     my $i = -1;
5759     my $node = $self->{open_elements}->[$i];
5760 wakaba 1.109 my $li_or_dtdd = {li => {li => 1},
5761     dt => {dt => 1, dd => 1},
5762     dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
5763 wakaba 1.52 LI: {
5764     ## Step 2
5765 wakaba 1.109 if ($li_or_dtdd->{$node->[1]}) {
5766 wakaba 1.52 if ($i != -1) {
5767 wakaba 1.79 !!!cp ('t355');
5768 wakaba 1.122 !!!parse-error (type => 'not closed',
5769     value => $self->{open_elements}->[-1]->[0]
5770     ->manakai_local_name,
5771     token => $token);
5772 wakaba 1.79 } else {
5773     !!!cp ('t356');
5774 wakaba 1.52 }
5775     splice @{$self->{open_elements}}, $i;
5776     last LI;
5777 wakaba 1.79 } else {
5778     !!!cp ('t357');
5779 wakaba 1.52 }
5780    
5781     ## Step 3
5782     if (not $formatting_category->{$node->[1]} and
5783     #not $phrasing_category->{$node->[1]} and
5784     ($special_category->{$node->[1]} or
5785     $scoping_category->{$node->[1]}) and
5786     $node->[1] ne 'address' and $node->[1] ne 'div') {
5787 wakaba 1.79 !!!cp ('t358');
5788 wakaba 1.52 last LI;
5789     }
5790    
5791 wakaba 1.79 !!!cp ('t359');
5792 wakaba 1.52 ## Step 4
5793     $i--;
5794     $node = $self->{open_elements}->[$i];
5795     redo LI;
5796     } # LI
5797    
5798 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5799 wakaba 1.52 !!!next-token;
5800 wakaba 1.53 redo B;
5801 wakaba 1.52 } elsif ($token->{tag_name} eq 'plaintext') {
5802     ## has a p element in scope
5803     INSCOPE: for (reverse @{$self->{open_elements}}) {
5804     if ($_->[1] eq 'p') {
5805 wakaba 1.79 !!!cp ('t367');
5806 wakaba 1.52 !!!back-token;
5807 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'p',
5808     line => $token->{line}, column => $token->{column}};
5809 wakaba 1.53 redo B;
5810 wakaba 1.52 } elsif ({
5811 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
5812 wakaba 1.52 button => 1, marquee => 1, object => 1, html => 1,
5813     }->{$_->[1]}) {
5814 wakaba 1.79 !!!cp ('t368');
5815 wakaba 1.52 last INSCOPE;
5816 wakaba 1.46 }
5817 wakaba 1.52 } # INSCOPE
5818    
5819 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5820 wakaba 1.52
5821     $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
5822    
5823     !!!next-token;
5824 wakaba 1.53 redo B;
5825 wakaba 1.52 } elsif ($token->{tag_name} eq 'a') {
5826     AFE: for my $i (reverse 0..$#$active_formatting_elements) {
5827     my $node = $active_formatting_elements->[$i];
5828     if ($node->[1] eq 'a') {
5829 wakaba 1.79 !!!cp ('t371');
5830 wakaba 1.113 !!!parse-error (type => 'in a:a', token => $token);
5831 wakaba 1.52
5832     !!!back-token;
5833 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'a',
5834     line => $token->{line}, column => $token->{column}};
5835 wakaba 1.113 $formatting_end_tag->($token);
5836 wakaba 1.52
5837     AFE2: for (reverse 0..$#$active_formatting_elements) {
5838     if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
5839 wakaba 1.79 !!!cp ('t372');
5840 wakaba 1.52 splice @$active_formatting_elements, $_, 1;
5841     last AFE2;
5842 wakaba 1.1 }
5843 wakaba 1.52 } # AFE2
5844     OE: for (reverse 0..$#{$self->{open_elements}}) {
5845     if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
5846 wakaba 1.79 !!!cp ('t373');
5847 wakaba 1.52 splice @{$self->{open_elements}}, $_, 1;
5848     last OE;
5849 wakaba 1.1 }
5850 wakaba 1.52 } # OE
5851     last AFE;
5852     } elsif ($node->[0] eq '#marker') {
5853 wakaba 1.79 !!!cp ('t374');
5854 wakaba 1.52 last AFE;
5855     }
5856     } # AFE
5857    
5858     $reconstruct_active_formatting_elements->($insert_to_current);
5859 wakaba 1.1
5860 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5861 wakaba 1.52 push @$active_formatting_elements, $self->{open_elements}->[-1];
5862 wakaba 1.1
5863 wakaba 1.52 !!!next-token;
5864 wakaba 1.53 redo B;
5865 wakaba 1.52 } elsif ($token->{tag_name} eq 'nobr') {
5866     $reconstruct_active_formatting_elements->($insert_to_current);
5867 wakaba 1.1
5868 wakaba 1.52 ## has a |nobr| element in scope
5869     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5870     my $node = $self->{open_elements}->[$_];
5871     if ($node->[1] eq 'nobr') {
5872 wakaba 1.79 !!!cp ('t376');
5873 wakaba 1.113 !!!parse-error (type => 'in nobr:nobr', token => $token);
5874 wakaba 1.52 !!!back-token;
5875 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
5876     line => $token->{line}, column => $token->{column}};
5877 wakaba 1.53 redo B;
5878 wakaba 1.52 } elsif ({
5879 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
5880 wakaba 1.52 button => 1, marquee => 1, object => 1, html => 1,
5881     }->{$node->[1]}) {
5882 wakaba 1.79 !!!cp ('t377');
5883 wakaba 1.52 last INSCOPE;
5884     }
5885     } # INSCOPE
5886    
5887 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5888 wakaba 1.52 push @$active_formatting_elements, $self->{open_elements}->[-1];
5889    
5890     !!!next-token;
5891 wakaba 1.53 redo B;
5892 wakaba 1.52 } elsif ($token->{tag_name} eq 'button') {
5893     ## has a button element in scope
5894     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5895     my $node = $self->{open_elements}->[$_];
5896     if ($node->[1] eq 'button') {
5897 wakaba 1.79 !!!cp ('t378');
5898 wakaba 1.113 !!!parse-error (type => 'in button:button', token => $token);
5899 wakaba 1.52 !!!back-token;
5900 wakaba 1.114 $token = {type => END_TAG_TOKEN, tag_name => 'button',
5901     line => $token->{line}, column => $token->{column}};
5902 wakaba 1.53 redo B;
5903 wakaba 1.52 } elsif ({
5904 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
5905 wakaba 1.52 button => 1, marquee => 1, object => 1, html => 1,
5906     }->{$node->[1]}) {
5907 wakaba 1.79 !!!cp ('t379');
5908 wakaba 1.52 last INSCOPE;
5909     }
5910     } # INSCOPE
5911    
5912     $reconstruct_active_formatting_elements->($insert_to_current);
5913    
5914 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5915 wakaba 1.85
5916     ## TODO: associate with $self->{form_element} if defined
5917    
5918 wakaba 1.52 push @$active_formatting_elements, ['#marker', ''];
5919 wakaba 1.1
5920 wakaba 1.52 !!!next-token;
5921 wakaba 1.53 redo B;
5922 wakaba 1.103 } elsif ({
5923 wakaba 1.109 xmp => 1,
5924     iframe => 1,
5925     noembed => 1,
5926     noframes => 1,
5927     noscript => 0, ## TODO: 1 if scripting is enabled
5928 wakaba 1.103 }->{$token->{tag_name}}) {
5929 wakaba 1.109 if ($token->{tag_name} eq 'xmp') {
5930     !!!cp ('t381');
5931     $reconstruct_active_formatting_elements->($insert_to_current);
5932     } else {
5933     !!!cp ('t399');
5934     }
5935     ## NOTE: There is an "as if in body" code clone.
5936 wakaba 1.96 $parse_rcdata->(CDATA_CONTENT_MODEL);
5937 wakaba 1.53 redo B;
5938 wakaba 1.52 } elsif ($token->{tag_name} eq 'isindex') {
5939 wakaba 1.113 !!!parse-error (type => 'isindex', token => $token);
5940 wakaba 1.52
5941     if (defined $self->{form_element}) {
5942 wakaba 1.79 !!!cp ('t389');
5943 wakaba 1.52 ## Ignore the token
5944     !!!next-token;
5945 wakaba 1.53 redo B;
5946 wakaba 1.52 } else {
5947     my $at = $token->{attributes};
5948     my $form_attrs;
5949     $form_attrs->{action} = $at->{action} if $at->{action};
5950     my $prompt_attr = $at->{prompt};
5951     $at->{name} = {name => 'name', value => 'isindex'};
5952     delete $at->{action};
5953     delete $at->{prompt};
5954     my @tokens = (
5955 wakaba 1.55 {type => START_TAG_TOKEN, tag_name => 'form',
5956 wakaba 1.114 attributes => $form_attrs,
5957     line => $token->{line}, column => $token->{column}},
5958     {type => START_TAG_TOKEN, tag_name => 'hr',
5959     line => $token->{line}, column => $token->{column}},
5960     {type => START_TAG_TOKEN, tag_name => 'p',
5961     line => $token->{line}, column => $token->{column}},
5962     {type => START_TAG_TOKEN, tag_name => 'label',
5963     line => $token->{line}, column => $token->{column}},
5964 wakaba 1.52 );
5965     if ($prompt_attr) {
5966 wakaba 1.79 !!!cp ('t390');
5967 wakaba 1.114 push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
5968 wakaba 1.118 #line => $token->{line}, column => $token->{column},
5969     };
5970 wakaba 1.1 } else {
5971 wakaba 1.79 !!!cp ('t391');
5972 wakaba 1.55 push @tokens, {type => CHARACTER_TOKEN,
5973 wakaba 1.114 data => 'This is a searchable index. Insert your search keywords here: ',
5974 wakaba 1.118 #line => $token->{line}, column => $token->{column},
5975     }; # SHOULD
5976 wakaba 1.52 ## TODO: make this configurable
5977 wakaba 1.1 }
5978 wakaba 1.52 push @tokens,
5979 wakaba 1.114 {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
5980     line => $token->{line}, column => $token->{column}},
5981 wakaba 1.55 #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
5982 wakaba 1.114 {type => END_TAG_TOKEN, tag_name => 'label',
5983     line => $token->{line}, column => $token->{column}},
5984     {type => END_TAG_TOKEN, tag_name => 'p',
5985     line => $token->{line}, column => $token->{column}},
5986     {type => START_TAG_TOKEN, tag_name => 'hr',
5987     line => $token->{line}, column => $token->{column}},
5988     {type => END_TAG_TOKEN, tag_name => 'form',
5989     line => $token->{line}, column => $token->{column}};
5990 wakaba 1.52 $token = shift @tokens;
5991     !!!back-token (@tokens);
5992 wakaba 1.53 redo B;
5993 wakaba 1.52 }
5994     } elsif ($token->{tag_name} eq 'textarea') {
5995     my $tag_name = $token->{tag_name};
5996     my $el;
5997 wakaba 1.116 !!!create-element ($el, $token->{tag_name}, $token->{attributes}, $token);
5998 wakaba 1.52
5999     ## TODO: $self->{form_element} if defined
6000     $self->{content_model} = RCDATA_CONTENT_MODEL;
6001     delete $self->{escape}; # MUST
6002    
6003     $insert->($el);
6004    
6005     my $text = '';
6006     !!!next-token;
6007 wakaba 1.55 if ($token->{type} == CHARACTER_TOKEN) {
6008 wakaba 1.52 $token->{data} =~ s/^\x0A//;
6009 wakaba 1.51 unless (length $token->{data}) {
6010 wakaba 1.79 !!!cp ('t392');
6011 wakaba 1.51 !!!next-token;
6012 wakaba 1.79 } else {
6013     !!!cp ('t393');
6014 wakaba 1.51 }
6015 wakaba 1.79 } else {
6016     !!!cp ('t394');
6017 wakaba 1.51 }
6018 wakaba 1.55 while ($token->{type} == CHARACTER_TOKEN) {
6019 wakaba 1.79 !!!cp ('t395');
6020 wakaba 1.52 $text .= $token->{data};
6021     !!!next-token;
6022     }
6023     if (length $text) {
6024 wakaba 1.79 !!!cp ('t396');
6025 wakaba 1.52 $el->manakai_append_text ($text);
6026     }
6027    
6028     $self->{content_model} = PCDATA_CONTENT_MODEL;
6029 wakaba 1.51
6030 wakaba 1.55 if ($token->{type} == END_TAG_TOKEN and
6031 wakaba 1.52 $token->{tag_name} eq $tag_name) {
6032 wakaba 1.79 !!!cp ('t397');
6033 wakaba 1.52 ## Ignore the token
6034     } else {
6035 wakaba 1.79 !!!cp ('t398');
6036 wakaba 1.113 !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);
6037 wakaba 1.51 }
6038 wakaba 1.52 !!!next-token;
6039 wakaba 1.53 redo B;
6040 wakaba 1.52 } elsif ({
6041     caption => 1, col => 1, colgroup => 1, frame => 1,
6042     frameset => 1, head => 1, option => 1, optgroup => 1,
6043     tbody => 1, td => 1, tfoot => 1, th => 1,
6044     thead => 1, tr => 1,
6045     }->{$token->{tag_name}}) {
6046 wakaba 1.79 !!!cp ('t401');
6047 wakaba 1.113 !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);
6048 wakaba 1.52 ## Ignore the token
6049     !!!next-token;
6050 wakaba 1.53 redo B;
6051 wakaba 1.52
6052     ## ISSUE: An issue on HTML5 new elements in the spec.
6053     } else {
6054 wakaba 1.110 if ($token->{tag_name} eq 'image') {
6055     !!!cp ('t384');
6056 wakaba 1.113 !!!parse-error (type => 'image', token => $token);
6057 wakaba 1.110 $token->{tag_name} = 'img';
6058     } else {
6059     !!!cp ('t385');
6060     }
6061    
6062     ## NOTE: There is an "as if <br>" code clone.
6063 wakaba 1.52 $reconstruct_active_formatting_elements->($insert_to_current);
6064    
6065 wakaba 1.116 !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6066 wakaba 1.109
6067 wakaba 1.110 if ({
6068     applet => 1, marquee => 1, object => 1,
6069     }->{$token->{tag_name}}) {
6070     !!!cp ('t380');
6071     push @$active_formatting_elements, ['#marker', ''];
6072     } elsif ({
6073     b => 1, big => 1, em => 1, font => 1, i => 1,
6074     s => 1, small => 1, strile => 1,
6075     strong => 1, tt => 1, u => 1,
6076     }->{$token->{tag_name}}) {
6077     !!!cp ('t375');
6078     push @$active_formatting_elements, $self->{open_elements}->[-1];
6079     } elsif ($token->{tag_name} eq 'input') {
6080     !!!cp ('t388');
6081     ## TODO: associate with $self->{form_element} if defined
6082     pop @{$self->{open_elements}};
6083     } elsif ({
6084     area => 1, basefont => 1, bgsound => 1, br => 1,
6085     embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
6086     #image => 1,
6087     }->{$token->{tag_name}}) {
6088     !!!cp ('t388.1');
6089     pop @{$self->{open_elements}};
6090     } elsif ($token->{tag_name} eq 'select') {
6091 wakaba 1.109 ## TODO: associate with $self->{form_element} if defined
6092    
6093     if ($self->{insertion_mode} & TABLE_IMS or
6094     $self->{insertion_mode} & BODY_TABLE_IMS or
6095     $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6096     !!!cp ('t400.1');
6097     $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
6098     } else {
6099     !!!cp ('t400.2');
6100     $self->{insertion_mode} = IN_SELECT_IM;
6101     }
6102 wakaba 1.110 } else {
6103     !!!cp ('t402');
6104 wakaba 1.109 }
6105 wakaba 1.51
6106 wakaba 1.52 !!!next-token;
6107 wakaba 1.53 redo B;
6108 wakaba 1.52 }
6109 wakaba 1.55 } elsif ($token->{type} == END_TAG_TOKEN) {
6110 wakaba 1.52 if ($token->{tag_name} eq 'body') {
6111 wakaba 1.107 ## has a |body| element in scope
6112     my $i;
6113 wakaba 1.111 INSCOPE: {
6114     for (reverse @{$self->{open_elements}}) {
6115     if ($_->[1] eq 'body') {
6116     !!!cp ('t405');
6117     $i = $_;
6118     last INSCOPE;
6119     } elsif ({
6120     applet => 1, table => 1, caption => 1, td => 1, th => 1,
6121     button => 1, marquee => 1, object => 1, html => 1,
6122     }->{$_->[1]}) {
6123     !!!cp ('t405.1');
6124     last;
6125     }
6126 wakaba 1.52 }
6127 wakaba 1.111
6128     !!!parse-error (type => 'start tag not allowed',
6129 wakaba 1.113 value => $token->{tag_name}, token => $token);
6130 wakaba 1.107 ## NOTE: Ignore the token.
6131 wakaba 1.52 !!!next-token;
6132 wakaba 1.53 redo B;
6133 wakaba 1.111 } # INSCOPE
6134 wakaba 1.107
6135     for (@{$self->{open_elements}}) {
6136     unless ({
6137     dd => 1, dt => 1, li => 1, p => 1, td => 1,
6138     th => 1, tr => 1, body => 1, html => 1,
6139     tbody => 1, tfoot => 1, thead => 1,
6140     }->{$_->[1]}) {
6141     !!!cp ('t403');
6142 wakaba 1.122 !!!parse-error (type => 'not closed',
6143     value => $_->[0]->manakai_local_name,
6144     token => $token);
6145 wakaba 1.107 last;
6146     } else {
6147     !!!cp ('t404');
6148     }
6149     }
6150    
6151     $self->{insertion_mode} = AFTER_BODY_IM;
6152     !!!next-token;
6153     redo B;
6154 wakaba 1.52 } elsif ($token->{tag_name} eq 'html') {
6155 wakaba 1.122 ## TODO: Update this code. It seems that the code below is not
6156     ## up-to-date, though it has same effect as speced.
6157 wakaba 1.52 if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {
6158     ## ISSUE: There is an issue in the spec.
6159     if ($self->{open_elements}->[-1]->[1] ne 'body') {
6160 wakaba 1.79 !!!cp ('t406');
6161 wakaba 1.122 !!!parse-error (type => 'not closed',
6162     value => $self->{open_elements}->[1]->[0]
6163     ->manakai_local_name,
6164     token => $token);
6165 wakaba 1.79 } else {
6166     !!!cp ('t407');
6167 wakaba 1.1 }
6168 wakaba 1.54 $self->{insertion_mode} = AFTER_BODY_IM;
6169 wakaba 1.52 ## reprocess
6170 wakaba 1.53 redo B;
6171 wakaba 1.51 } else {
6172 wakaba 1.79 !!!cp ('t408');
6173 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6174 wakaba 1.52 ## Ignore the token
6175     !!!next-token;
6176 wakaba 1.53 redo B;
6177 wakaba 1.51 }
6178 wakaba 1.52 } elsif ({
6179     address => 1, blockquote => 1, center => 1, dir => 1,
6180     div => 1, dl => 1, fieldset => 1, listing => 1,
6181     menu => 1, ol => 1, pre => 1, ul => 1,
6182     dd => 1, dt => 1, li => 1,
6183 wakaba 1.103 applet => 1, button => 1, marquee => 1, object => 1,
6184 wakaba 1.52 }->{$token->{tag_name}}) {
6185     ## has an element in scope
6186     my $i;
6187     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6188     my $node = $self->{open_elements}->[$_];
6189     if ($node->[1] eq $token->{tag_name}) {
6190 wakaba 1.79 !!!cp ('t410');
6191 wakaba 1.52 $i = $_;
6192 wakaba 1.87 last INSCOPE;
6193 wakaba 1.52 } elsif ({
6194 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
6195 wakaba 1.52 button => 1, marquee => 1, object => 1, html => 1,
6196     }->{$node->[1]}) {
6197 wakaba 1.79 !!!cp ('t411');
6198 wakaba 1.52 last INSCOPE;
6199 wakaba 1.51 }
6200 wakaba 1.52 } # INSCOPE
6201 wakaba 1.89
6202     unless (defined $i) { # has an element in scope
6203     !!!cp ('t413');
6204 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6205 wakaba 1.89 } else {
6206     ## Step 1. generate implied end tags
6207     while ({
6208     dd => ($token->{tag_name} ne 'dd'),
6209     dt => ($token->{tag_name} ne 'dt'),
6210     li => ($token->{tag_name} ne 'li'),
6211     p => 1,
6212     }->{$self->{open_elements}->[-1]->[1]}) {
6213     !!!cp ('t409');
6214     pop @{$self->{open_elements}};
6215     }
6216    
6217     ## Step 2.
6218     if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
6219 wakaba 1.79 !!!cp ('t412');
6220 wakaba 1.122 !!!parse-error (type => 'not closed',
6221     value => $self->{open_elements}->[-1]->[0]
6222     ->manakai_local_name,
6223     token => $token);
6224 wakaba 1.51 } else {
6225 wakaba 1.89 !!!cp ('t414');
6226 wakaba 1.51 }
6227 wakaba 1.89
6228     ## Step 3.
6229 wakaba 1.52 splice @{$self->{open_elements}}, $i;
6230 wakaba 1.89
6231     ## Step 4.
6232     $clear_up_to_marker->()
6233     if {
6234 wakaba 1.103 applet => 1, button => 1, marquee => 1, object => 1,
6235 wakaba 1.89 }->{$token->{tag_name}};
6236 wakaba 1.51 }
6237 wakaba 1.52 !!!next-token;
6238 wakaba 1.53 redo B;
6239 wakaba 1.52 } elsif ($token->{tag_name} eq 'form') {
6240 wakaba 1.92 undef $self->{form_element};
6241    
6242 wakaba 1.52 ## has an element in scope
6243 wakaba 1.92 my $i;
6244 wakaba 1.52 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6245     my $node = $self->{open_elements}->[$_];
6246     if ($node->[1] eq $token->{tag_name}) {
6247 wakaba 1.79 !!!cp ('t418');
6248 wakaba 1.92 $i = $_;
6249 wakaba 1.52 last INSCOPE;
6250     } elsif ({
6251 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
6252 wakaba 1.52 button => 1, marquee => 1, object => 1, html => 1,
6253     }->{$node->[1]}) {
6254 wakaba 1.79 !!!cp ('t419');
6255 wakaba 1.52 last INSCOPE;
6256     }
6257     } # INSCOPE
6258 wakaba 1.92
6259     unless (defined $i) { # has an element in scope
6260 wakaba 1.79 !!!cp ('t421');
6261 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6262 wakaba 1.92 } else {
6263     ## Step 1. generate implied end tags
6264     while ({
6265     dd => 1, dt => 1, li => 1, p => 1,
6266     }->{$self->{open_elements}->[-1]->[1]}) {
6267     !!!cp ('t417');
6268     pop @{$self->{open_elements}};
6269     }
6270    
6271     ## Step 2.
6272     if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
6273     !!!cp ('t417.1');
6274 wakaba 1.122 !!!parse-error (type => 'not closed',
6275     value => $self->{open_elements}->[-1]->[0]
6276     ->manakai_local_name,
6277     token => $token);
6278 wakaba 1.92 } else {
6279     !!!cp ('t420');
6280     }
6281    
6282     ## Step 3.
6283     splice @{$self->{open_elements}}, $i;
6284 wakaba 1.52 }
6285    
6286     !!!next-token;
6287 wakaba 1.53 redo B;
6288 wakaba 1.52 } elsif ({
6289     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6290     }->{$token->{tag_name}}) {
6291     ## has an element in scope
6292     my $i;
6293     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6294     my $node = $self->{open_elements}->[$_];
6295     if ({
6296     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6297     }->{$node->[1]}) {
6298 wakaba 1.79 !!!cp ('t423');
6299 wakaba 1.52 $i = $_;
6300     last INSCOPE;
6301     } elsif ({
6302 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
6303 wakaba 1.52 button => 1, marquee => 1, object => 1, html => 1,
6304     }->{$node->[1]}) {
6305 wakaba 1.79 !!!cp ('t424');
6306 wakaba 1.52 last INSCOPE;
6307 wakaba 1.51 }
6308 wakaba 1.52 } # INSCOPE
6309 wakaba 1.93
6310     unless (defined $i) { # has an element in scope
6311     !!!cp ('t425.1');
6312 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6313 wakaba 1.79 } else {
6314 wakaba 1.93 ## Step 1. generate implied end tags
6315     while ({
6316     dd => 1, dt => 1, li => 1, p => 1,
6317     }->{$self->{open_elements}->[-1]->[1]}) {
6318     !!!cp ('t422');
6319     pop @{$self->{open_elements}};
6320     }
6321    
6322     ## Step 2.
6323     if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
6324     !!!cp ('t425');
6325 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6326 wakaba 1.93 } else {
6327     !!!cp ('t426');
6328     }
6329    
6330     ## Step 3.
6331     splice @{$self->{open_elements}}, $i;
6332 wakaba 1.36 }
6333 wakaba 1.52
6334     !!!next-token;
6335 wakaba 1.53 redo B;
6336 wakaba 1.87 } elsif ($token->{tag_name} eq 'p') {
6337     ## has an element in scope
6338     my $i;
6339     INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6340     my $node = $self->{open_elements}->[$_];
6341     if ($node->[1] eq $token->{tag_name}) {
6342     !!!cp ('t410.1');
6343     $i = $_;
6344 wakaba 1.88 last INSCOPE;
6345 wakaba 1.87 } elsif ({
6346 wakaba 1.103 applet => 1, table => 1, caption => 1, td => 1, th => 1,
6347 wakaba 1.87 button => 1, marquee => 1, object => 1, html => 1,
6348     }->{$node->[1]}) {
6349     !!!cp ('t411.1');
6350     last INSCOPE;
6351     }
6352     } # INSCOPE
6353 wakaba 1.91
6354     if (defined $i) {
6355     if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
6356 wakaba 1.87 !!!cp ('t412.1');
6357 wakaba 1.122 !!!parse-error (type => 'not closed',
6358     value => $self->{open_elements}->[-1]->[0]
6359     ->manakai_local_name,
6360     token => $token);
6361 wakaba 1.87 } else {
6362 wakaba 1.91 !!!cp ('t414.1');
6363 wakaba 1.87 }
6364 wakaba 1.91
6365 wakaba 1.87 splice @{$self->{open_elements}}, $i;
6366     } else {
6367 wakaba 1.91 !!!cp ('t413.1');
6368 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6369 wakaba 1.91
6370 wakaba 1.87 !!!cp ('t415.1');
6371     ## As if <p>, then reprocess the current token
6372     my $el;
6373 wakaba 1.116 !!!create-element ($el, 'p',, $token);
6374 wakaba 1.87 $insert->($el);
6375 wakaba 1.91 ## NOTE: Not inserted into |$self->{open_elements}|.
6376 wakaba 1.87 }
6377 wakaba 1.91
6378 wakaba 1.87 !!!next-token;
6379     redo B;
6380 wakaba 1.52 } elsif ({
6381     a => 1,
6382     b => 1, big => 1, em => 1, font => 1, i => 1,
6383     nobr => 1, s => 1, small => 1, strile => 1,
6384     strong => 1, tt => 1, u => 1,
6385     }->{$token->{tag_name}}) {
6386 wakaba 1.79 !!!cp ('t427');
6387 wakaba 1.113 $formatting_end_tag->($token);
6388 wakaba 1.53 redo B;
6389 wakaba 1.52 } elsif ($token->{tag_name} eq 'br') {
6390 wakaba 1.79 !!!cp ('t428');
6391 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:br', token => $token);
6392 wakaba 1.52
6393     ## As if <br>
6394     $reconstruct_active_formatting_elements->($insert_to_current);
6395    
6396     my $el;
6397 wakaba 1.116 !!!create-element ($el, 'br',, $token);
6398 wakaba 1.52 $insert->($el);
6399    
6400     ## Ignore the token.
6401     !!!next-token;
6402 wakaba 1.53 redo B;
6403 wakaba 1.52 } elsif ({
6404     caption => 1, col => 1, colgroup => 1, frame => 1,
6405     frameset => 1, head => 1, option => 1, optgroup => 1,
6406     tbody => 1, td => 1, tfoot => 1, th => 1,
6407     thead => 1, tr => 1,
6408     area => 1, basefont => 1, bgsound => 1,
6409     embed => 1, hr => 1, iframe => 1, image => 1,
6410     img => 1, input => 1, isindex => 1, noembed => 1,
6411     noframes => 1, param => 1, select => 1, spacer => 1,
6412     table => 1, textarea => 1, wbr => 1,
6413     noscript => 0, ## TODO: if scripting is enabled
6414     }->{$token->{tag_name}}) {
6415 wakaba 1.79 !!!cp ('t429');
6416 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6417 wakaba 1.52 ## Ignore the token
6418     !!!next-token;
6419 wakaba 1.53 redo B;
6420 wakaba 1.52
6421     ## ISSUE: Issue on HTML5 new elements in spec
6422    
6423     } else {
6424     ## Step 1
6425     my $node_i = -1;
6426     my $node = $self->{open_elements}->[$node_i];
6427 wakaba 1.51
6428 wakaba 1.52 ## Step 2
6429     S2: {
6430     if ($node->[1] eq $token->{tag_name}) {
6431     ## Step 1
6432     ## generate implied end tags
6433 wakaba 1.86 while ({
6434     dd => 1, dt => 1, li => 1, p => 1,
6435     }->{$self->{open_elements}->[-1]->[1]}) {
6436 wakaba 1.79 !!!cp ('t430');
6437 wakaba 1.83 ## ISSUE: Can this case be reached?
6438 wakaba 1.86 pop @{$self->{open_elements}};
6439 wakaba 1.52 }
6440    
6441     ## Step 2
6442     if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {
6443 wakaba 1.79 !!!cp ('t431');
6444 wakaba 1.58 ## NOTE: <x><y></x>
6445 wakaba 1.122 !!!parse-error (type => 'not closed',
6446     value => $self->{open_elements}->[-1]->[0]
6447     ->manakai_local_name,
6448     token => $token);
6449 wakaba 1.79 } else {
6450     !!!cp ('t432');
6451 wakaba 1.52 }
6452    
6453     ## Step 3
6454     splice @{$self->{open_elements}}, $node_i;
6455 wakaba 1.51
6456 wakaba 1.1 !!!next-token;
6457 wakaba 1.52 last S2;
6458 wakaba 1.1 } else {
6459 wakaba 1.52 ## Step 3
6460     if (not $formatting_category->{$node->[1]} and
6461     #not $phrasing_category->{$node->[1]} and
6462     ($special_category->{$node->[1]} or
6463     $scoping_category->{$node->[1]})) {
6464 wakaba 1.79 !!!cp ('t433');
6465 wakaba 1.113 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6466 wakaba 1.52 ## Ignore the token
6467     !!!next-token;
6468     last S2;
6469     }
6470 wakaba 1.79
6471     !!!cp ('t434');
6472 wakaba 1.1 }
6473 wakaba 1.52
6474     ## Step 4
6475     $node_i--;
6476     $node = $self->{open_elements}->[$node_i];
6477    
6478     ## Step 5;
6479     redo S2;
6480     } # S2
6481 wakaba 1.53 redo B;
6482 wakaba 1.1 }
6483     }
6484 wakaba 1.52 redo B;
6485 wakaba 1.1 } # B
6486    
6487     ## Stop parsing # MUST
6488    
6489     ## TODO: script stuffs
6490 wakaba 1.3 } # _tree_construct_main
6491    
6492     sub set_inner_html ($$$) {
6493     my $class = shift;
6494     my $node = shift;
6495     my $s = \$_[0];
6496     my $onerror = $_[1];
6497    
6498 wakaba 1.63 ## ISSUE: Should {confident} be true?
6499    
6500 wakaba 1.3 my $nt = $node->node_type;
6501     if ($nt == 9) {
6502     # MUST
6503    
6504     ## Step 1 # MUST
6505     ## TODO: If the document has an active parser, ...
6506     ## ISSUE: There is an issue in the spec.
6507    
6508     ## Step 2 # MUST
6509     my @cn = @{$node->child_nodes};
6510     for (@cn) {
6511     $node->remove_child ($_);
6512     }
6513    
6514     ## Step 3, 4, 5 # MUST
6515     $class->parse_string ($$s => $node, $onerror);
6516     } elsif ($nt == 1) {
6517     ## TODO: If non-html element
6518    
6519     ## NOTE: Most of this code is copied from |parse_string|
6520    
6521     ## Step 1 # MUST
6522 wakaba 1.14 my $this_doc = $node->owner_document;
6523     my $doc = $this_doc->implementation->create_document;
6524 wakaba 1.18 $doc->manakai_is_html (1);
6525 wakaba 1.3 my $p = $class->new;
6526     $p->{document} = $doc;
6527    
6528 wakaba 1.84 ## Step 8 # MUST
6529 wakaba 1.3 my $i = 0;
6530 wakaba 1.121 $p->{line_prev} = $p->{line} = 1;
6531     $p->{column_prev} = $p->{column} = 0;
6532 wakaba 1.76 $p->{set_next_char} = sub {
6533 wakaba 1.3 my $self = shift;
6534 wakaba 1.14
6535 wakaba 1.76 pop @{$self->{prev_char}};
6536     unshift @{$self->{prev_char}}, $self->{next_char};
6537 wakaba 1.14
6538 wakaba 1.76 $self->{next_char} = -1 and return if $i >= length $$s;
6539     $self->{next_char} = ord substr $$s, $i++, 1;
6540 wakaba 1.121
6541     ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
6542     $p->{column}++;
6543 wakaba 1.4
6544 wakaba 1.76 if ($self->{next_char} == 0x000A) { # LF
6545 wakaba 1.121 $p->{line}++;
6546     $p->{column} = 0;
6547 wakaba 1.79 !!!cp ('i1');
6548 wakaba 1.76 } elsif ($self->{next_char} == 0x000D) { # CR
6549 wakaba 1.15 $i++ if substr ($$s, $i, 1) eq "\x0A";
6550 wakaba 1.76 $self->{next_char} = 0x000A; # LF # MUST
6551 wakaba 1.121 $p->{line}++;
6552     $p->{column} = 0;
6553 wakaba 1.79 !!!cp ('i2');
6554 wakaba 1.76 } elsif ($self->{next_char} > 0x10FFFF) {
6555     $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
6556 wakaba 1.79 !!!cp ('i3');
6557 wakaba 1.76 } elsif ($self->{next_char} == 0x0000) { # NULL
6558 wakaba 1.79 !!!cp ('i4');
6559 wakaba 1.14 !!!parse-error (type => 'NULL');
6560 wakaba 1.76 $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
6561 wakaba 1.3 }
6562     };
6563 wakaba 1.76 $p->{prev_char} = [-1, -1, -1];
6564     $p->{next_char} = -1;
6565 wakaba 1.3
6566     my $ponerror = $onerror || sub {
6567     my (%opt) = @_;
6568 wakaba 1.121 my $line = $opt{line};
6569     my $column = $opt{column};
6570     if (defined $opt{token} and defined $opt{token}->{line}) {
6571     $line = $opt{token}->{line};
6572     $column = $opt{token}->{column};
6573     }
6574     warn "Parse error ($opt{type}) at line $line column $column\n";
6575 wakaba 1.3 };
6576     $p->{parse_error} = sub {
6577 wakaba 1.121 $ponerror->(line => $p->{line}, column => $p->{column}, @_);
6578 wakaba 1.3 };
6579    
6580     $p->_initialize_tokenizer;
6581     $p->_initialize_tree_constructor;
6582    
6583     ## Step 2
6584 wakaba 1.71 my $node_ln = $node->manakai_local_name;
6585 wakaba 1.40 $p->{content_model} = {
6586     title => RCDATA_CONTENT_MODEL,
6587     textarea => RCDATA_CONTENT_MODEL,
6588     style => CDATA_CONTENT_MODEL,
6589     script => CDATA_CONTENT_MODEL,
6590     xmp => CDATA_CONTENT_MODEL,
6591     iframe => CDATA_CONTENT_MODEL,
6592     noembed => CDATA_CONTENT_MODEL,
6593     noframes => CDATA_CONTENT_MODEL,
6594     noscript => CDATA_CONTENT_MODEL,
6595     plaintext => PLAINTEXT_CONTENT_MODEL,
6596     }->{$node_ln};
6597     $p->{content_model} = PCDATA_CONTENT_MODEL
6598     unless defined $p->{content_model};
6599     ## ISSUE: What is "the name of the element"? local name?
6600 wakaba 1.3
6601     $p->{inner_html_node} = [$node, $node_ln];
6602    
6603 wakaba 1.84 ## Step 3
6604 wakaba 1.3 my $root = $doc->create_element_ns
6605     ('http://www.w3.org/1999/xhtml', [undef, 'html']);
6606    
6607 wakaba 1.84 ## Step 4 # MUST
6608 wakaba 1.3 $doc->append_child ($root);
6609    
6610 wakaba 1.84 ## Step 5 # MUST
6611 wakaba 1.3 push @{$p->{open_elements}}, [$root, 'html'];
6612    
6613     undef $p->{head_element};
6614    
6615 wakaba 1.84 ## Step 6 # MUST
6616 wakaba 1.3 $p->_reset_insertion_mode;
6617    
6618 wakaba 1.84 ## Step 7 # MUST
6619 wakaba 1.3 my $anode = $node;
6620     AN: while (defined $anode) {
6621     if ($anode->node_type == 1) {
6622     my $nsuri = $anode->namespace_uri;
6623     if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
6624 wakaba 1.71 if ($anode->manakai_local_name eq 'form') {
6625 wakaba 1.79 !!!cp ('i5');
6626 wakaba 1.3 $p->{form_element} = $anode;
6627     last AN;
6628     }
6629     }
6630     }
6631     $anode = $anode->parent_node;
6632     } # AN
6633    
6634 wakaba 1.84 ## Step 9 # MUST
6635 wakaba 1.3 {
6636     my $self = $p;
6637     !!!next-token;
6638     }
6639     $p->_tree_construction_main;
6640    
6641 wakaba 1.84 ## Step 10 # MUST
6642 wakaba 1.3 my @cn = @{$node->child_nodes};
6643     for (@cn) {
6644     $node->remove_child ($_);
6645     }
6646     ## ISSUE: mutation events? read-only?
6647    
6648 wakaba 1.84 ## Step 11 # MUST
6649 wakaba 1.3 @cn = @{$root->child_nodes};
6650     for (@cn) {
6651 wakaba 1.14 $this_doc->adopt_node ($_);
6652 wakaba 1.3 $node->append_child ($_);
6653     }
6654 wakaba 1.14 ## ISSUE: mutation events?
6655 wakaba 1.3
6656     $p->_terminate_tree_constructor;
6657 wakaba 1.121
6658     delete $p->{parse_error}; # delete loop
6659 wakaba 1.3 } else {
6660     die "$0: |set_inner_html| is not defined for node of type $nt";
6661     }
6662     } # set_inner_html
6663    
6664     } # tree construction stage
6665 wakaba 1.1
6666 wakaba 1.63 package Whatpm::HTML::RestartParser;
6667     push our @ISA, 'Error';
6668    
6669 wakaba 1.1 1;
6670 wakaba 1.122 # $Date: 2008/03/20 08:04:58 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24