/[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.139 - (show annotations) (download) (as text)
Sat May 24 04:26:27 2008 UTC (18 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.138: +86 -45 lines
File MIME type: application/x-wais-source
++ whatpm/t/ChangeLog	24 May 2008 03:20:16 -0000
2008-05-24  Wakaba  <wakaba@suika.fam.cx>

	* tree-test-1.dat: Test data related to changes in HTML5
	revision 1686 are added.

++ whatpm/Whatpm/ChangeLog	24 May 2008 04:24:26 -0000
2008-05-24  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm.src (_tree_construction_main): Change handling of
	end tags in head insertion modes (HTML5 revision 1686).
	(parse_char_string): Bug fix for non-utf8 character string handlings.
	(parse_char_stream): |ungetc| does not work well for this context.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24