/[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.135 - (show annotations) (download) (as text)
Sat May 17 07:31:49 2008 UTC (18 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.134: +18 -9 lines
File MIME type: application/x-wais-source
++ whatpm/Whatpm/ChangeLog	17 May 2008 07:31:24 -0000
	* HTML.pm.src (parse_char_stream): New method.
	(parse_char_string): This method is now defined as an invocation
	of the |parse_char_stream| method.

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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24