/[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.147 - (show annotations) (download) (as text)
Sun May 25 06:03:57 2008 UTC (18 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.146: +4 -3 lines
File MIME type: application/x-wais-source
++ whatpm/t/ChangeLog	25 May 2008 05:59:46 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* tree-test-1.dat: Test data on <isindex/> are added (HTML5
	revision 1697).

++ whatpm/Whatpm/ChangeLog	25 May 2008 06:00:14 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm.src: Don't raise parse error for <isindex/> (HTML5
	revision 1697).

++ whatpm/Whatpm/ContentChecker/ChangeLog	25 May 2008 02:05:07 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: The |param| element is in LC phase now.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24