/[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.186 - (show annotations) (download) (as text)
Sat Sep 20 07:54:47 2008 UTC (18 years ago) by wakaba
Branch: MAIN
Changes since 1.185: +6 -5 lines
File MIME type: application/x-wais-source
++ whatpm/t/ChangeLog	20 Sep 2008 07:54:41 -0000
	* ContentType.t: Updated to disallow 0x0B as text byte (HTML5
	revision 1738).

2008-09-20  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	20 Sep 2008 07:39:30 -0000
	* ContentType.pm: 0x0B is no longer a space character (HTML5
	revision 1738).

	* HTML.pm.src: U+000B is no longer a space character for the
	algorithm for extracting an encoding from a Content-Type (HTML5
	revision 1738).

2008-09-20  Wakaba  <wakaba@suika.fam.cx>

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24