/[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.178 - (show annotations) (download) (as text)
Sun Sep 14 11:57:41 2008 UTC (18 years ago) by wakaba
Branch: MAIN
Changes since 1.177: +33 -16 lines
File MIME type: application/x-wais-source
++ whatpm/Whatpm/ChangeLog	14 Sep 2008 11:56:24 -0000
	* HTML.pm.src: Use |read| instead of |getc|.  |set_inner_html|
	would report character error from now.

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

++ whatpm/Whatpm/Charset/ChangeLog	14 Sep 2008 11:57:38 -0000
	* DecodeHandle.pm (CharString onerror): New method.

	* UnicodeString.pm (read): New.
	(getc): Removed.
	(manakai_read_until): Checking operation implemented.

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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24