Parent Directory
|
Revision Log
|
Patch
| revision 1.106 by wakaba, Sun Mar 9 10:31:19 2008 UTC | revision 1.136 by wakaba, Sat May 17 12:29:24 2008 UTC | |
|---|---|---|
| # | Line 8 use Error qw(:try); | Line 8 use Error qw(:try); |
| 8 | ## doc.write (''); | ## doc.write (''); |
| 9 | ## alert (doc.compatMode); | ## alert (doc.compatMode); |
| 10 | ||
| ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263) | ||
| 11 | ## TODO: 1252 parse error (revision 1264) | ## TODO: 1252 parse error (revision 1264) |
| 12 | ## TODO: 8859-11 = 874 (revision 1271) | ## TODO: 8859-11 = 874 (revision 1271) |
| 13 | ||
| 14 | my $permitted_slash_tag_name = { | my $HTML_NS = q<http://www.w3.org/1999/xhtml>; |
| 15 | base => 1, | my $MML_NS = q<http://www.w3.org/1998/Math/MathML>; |
| 16 | link => 1, | my $SVG_NS = q<http://www.w3.org/2000/svg>; |
| 17 | meta => 1, | my $XLINK_NS = q<http://www.w3.org/1999/xlink>; |
| 18 | hr => 1, | my $XML_NS = q<http://www.w3.org/XML/1998/namespace>; |
| 19 | br => 1, | my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>; |
| 20 | img => 1, | |
| 21 | embed => 1, | sub A_EL () { 0b1 } |
| 22 | param => 1, | sub ADDRESS_EL () { 0b10 } |
| 23 | area => 1, | sub BODY_EL () { 0b100 } |
| 24 | col => 1, | sub BUTTON_EL () { 0b1000 } |
| 25 | input => 1, | sub CAPTION_EL () { 0b10000 } |
| 26 | sub DD_EL () { 0b100000 } | |
| 27 | sub DIV_EL () { 0b1000000 } | |
| 28 | sub DT_EL () { 0b10000000 } | |
| 29 | sub FORM_EL () { 0b100000000 } | |
| 30 | sub FORMATTING_EL () { 0b1000000000 } | |
| 31 | sub FRAMESET_EL () { 0b10000000000 } | |
| 32 | sub HEADING_EL () { 0b100000000000 } | |
| 33 | sub HTML_EL () { 0b1000000000000 } | |
| 34 | sub LI_EL () { 0b10000000000000 } | |
| 35 | sub NOBR_EL () { 0b100000000000000 } | |
| 36 | sub OPTION_EL () { 0b1000000000000000 } | |
| 37 | sub OPTGROUP_EL () { 0b10000000000000000 } | |
| 38 | sub P_EL () { 0b100000000000000000 } | |
| 39 | sub SELECT_EL () { 0b1000000000000000000 } | |
| 40 | sub TABLE_EL () { 0b10000000000000000000 } | |
| 41 | sub TABLE_CELL_EL () { 0b100000000000000000000 } | |
| 42 | sub TABLE_ROW_EL () { 0b1000000000000000000000 } | |
| 43 | sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 } | |
| 44 | sub MISC_SCOPING_EL () { 0b100000000000000000000000 } | |
| 45 | sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 } | |
| 46 | sub FOREIGN_EL () { 0b10000000000000000000000000 } | |
| 47 | sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 } | |
| 48 | sub MML_AXML_EL () { 0b1000000000000000000000000000 } | |
| 49 | ||
| 50 | sub TABLE_ROWS_EL () { | |
| 51 | TABLE_EL | | |
| 52 | TABLE_ROW_EL | | |
| 53 | TABLE_ROW_GROUP_EL | |
| 54 | } | |
| 55 | ||
| 56 | sub END_TAG_OPTIONAL_EL () { | |
| 57 | DD_EL | | |
| 58 | DT_EL | | |
| 59 | LI_EL | | |
| 60 | P_EL | |
| 61 | } | |
| 62 | ||
| 63 | sub ALL_END_TAG_OPTIONAL_EL () { | |
| 64 | END_TAG_OPTIONAL_EL | | |
| 65 | BODY_EL | | |
| 66 | HTML_EL | | |
| 67 | TABLE_CELL_EL | | |
| 68 | TABLE_ROW_EL | | |
| 69 | TABLE_ROW_GROUP_EL | |
| 70 | } | |
| 71 | ||
| 72 | sub SCOPING_EL () { | |
| 73 | BUTTON_EL | | |
| 74 | CAPTION_EL | | |
| 75 | HTML_EL | | |
| 76 | TABLE_EL | | |
| 77 | TABLE_CELL_EL | | |
| 78 | MISC_SCOPING_EL | |
| 79 | } | |
| 80 | ||
| 81 | sub TABLE_SCOPING_EL () { | |
| 82 | HTML_EL | | |
| 83 | TABLE_EL | |
| 84 | } | |
| 85 | ||
| 86 | sub TABLE_ROWS_SCOPING_EL () { | |
| 87 | HTML_EL | | |
| 88 | TABLE_ROW_GROUP_EL | |
| 89 | } | |
| 90 | ||
| 91 | sub TABLE_ROW_SCOPING_EL () { | |
| 92 | HTML_EL | | |
| 93 | TABLE_ROW_EL | |
| 94 | } | |
| 95 | ||
| 96 | sub SPECIAL_EL () { | |
| 97 | ADDRESS_EL | | |
| 98 | BODY_EL | | |
| 99 | DIV_EL | | |
| 100 | END_TAG_OPTIONAL_EL | | |
| 101 | FORM_EL | | |
| 102 | FRAMESET_EL | | |
| 103 | HEADING_EL | | |
| 104 | OPTION_EL | | |
| 105 | OPTGROUP_EL | | |
| 106 | SELECT_EL | | |
| 107 | TABLE_ROW_EL | | |
| 108 | TABLE_ROW_GROUP_EL | | |
| 109 | MISC_SPECIAL_EL | |
| 110 | } | |
| 111 | ||
| 112 | my $el_category = { | |
| 113 | a => A_EL | FORMATTING_EL, | |
| 114 | address => ADDRESS_EL, | |
| 115 | applet => MISC_SCOPING_EL, | |
| 116 | area => MISC_SPECIAL_EL, | |
| 117 | b => FORMATTING_EL, | |
| 118 | base => MISC_SPECIAL_EL, | |
| 119 | basefont => MISC_SPECIAL_EL, | |
| 120 | bgsound => MISC_SPECIAL_EL, | |
| 121 | big => FORMATTING_EL, | |
| 122 | blockquote => MISC_SPECIAL_EL, | |
| 123 | body => BODY_EL, | |
| 124 | br => MISC_SPECIAL_EL, | |
| 125 | button => BUTTON_EL, | |
| 126 | caption => CAPTION_EL, | |
| 127 | center => MISC_SPECIAL_EL, | |
| 128 | col => MISC_SPECIAL_EL, | |
| 129 | colgroup => MISC_SPECIAL_EL, | |
| 130 | dd => DD_EL, | |
| 131 | dir => MISC_SPECIAL_EL, | |
| 132 | div => DIV_EL, | |
| 133 | dl => MISC_SPECIAL_EL, | |
| 134 | dt => DT_EL, | |
| 135 | em => FORMATTING_EL, | |
| 136 | embed => MISC_SPECIAL_EL, | |
| 137 | fieldset => MISC_SPECIAL_EL, | |
| 138 | font => FORMATTING_EL, | |
| 139 | form => FORM_EL, | |
| 140 | frame => MISC_SPECIAL_EL, | |
| 141 | frameset => FRAMESET_EL, | |
| 142 | h1 => HEADING_EL, | |
| 143 | h2 => HEADING_EL, | |
| 144 | h3 => HEADING_EL, | |
| 145 | h4 => HEADING_EL, | |
| 146 | h5 => HEADING_EL, | |
| 147 | h6 => HEADING_EL, | |
| 148 | head => MISC_SPECIAL_EL, | |
| 149 | hr => MISC_SPECIAL_EL, | |
| 150 | html => HTML_EL, | |
| 151 | i => FORMATTING_EL, | |
| 152 | iframe => MISC_SPECIAL_EL, | |
| 153 | img => MISC_SPECIAL_EL, | |
| 154 | input => MISC_SPECIAL_EL, | |
| 155 | isindex => MISC_SPECIAL_EL, | |
| 156 | li => LI_EL, | |
| 157 | link => MISC_SPECIAL_EL, | |
| 158 | listing => MISC_SPECIAL_EL, | |
| 159 | marquee => MISC_SCOPING_EL, | |
| 160 | menu => MISC_SPECIAL_EL, | |
| 161 | meta => MISC_SPECIAL_EL, | |
| 162 | nobr => NOBR_EL | FORMATTING_EL, | |
| 163 | noembed => MISC_SPECIAL_EL, | |
| 164 | noframes => MISC_SPECIAL_EL, | |
| 165 | noscript => MISC_SPECIAL_EL, | |
| 166 | object => MISC_SCOPING_EL, | |
| 167 | ol => MISC_SPECIAL_EL, | |
| 168 | optgroup => OPTGROUP_EL, | |
| 169 | option => OPTION_EL, | |
| 170 | p => P_EL, | |
| 171 | param => MISC_SPECIAL_EL, | |
| 172 | plaintext => MISC_SPECIAL_EL, | |
| 173 | pre => MISC_SPECIAL_EL, | |
| 174 | s => FORMATTING_EL, | |
| 175 | script => MISC_SPECIAL_EL, | |
| 176 | select => SELECT_EL, | |
| 177 | small => FORMATTING_EL, | |
| 178 | spacer => MISC_SPECIAL_EL, | |
| 179 | strike => FORMATTING_EL, | |
| 180 | strong => FORMATTING_EL, | |
| 181 | style => MISC_SPECIAL_EL, | |
| 182 | table => TABLE_EL, | |
| 183 | tbody => TABLE_ROW_GROUP_EL, | |
| 184 | td => TABLE_CELL_EL, | |
| 185 | textarea => MISC_SPECIAL_EL, | |
| 186 | tfoot => TABLE_ROW_GROUP_EL, | |
| 187 | th => TABLE_CELL_EL, | |
| 188 | thead => TABLE_ROW_GROUP_EL, | |
| 189 | title => MISC_SPECIAL_EL, | |
| 190 | tr => TABLE_ROW_EL, | |
| 191 | tt => FORMATTING_EL, | |
| 192 | u => FORMATTING_EL, | |
| 193 | ul => MISC_SPECIAL_EL, | |
| 194 | wbr => MISC_SPECIAL_EL, | |
| 195 | }; | }; |
| 196 | ||
| 197 | my $el_category_f = { | |
| 198 | $MML_NS => { | |
| 199 | 'annotation-xml' => MML_AXML_EL, | |
| 200 | mi => FOREIGN_FLOW_CONTENT_EL, | |
| 201 | mo => FOREIGN_FLOW_CONTENT_EL, | |
| 202 | mn => FOREIGN_FLOW_CONTENT_EL, | |
| 203 | ms => FOREIGN_FLOW_CONTENT_EL, | |
| 204 | mtext => FOREIGN_FLOW_CONTENT_EL, | |
| 205 | }, | |
| 206 | $SVG_NS => { | |
| 207 | foreignObject => FOREIGN_FLOW_CONTENT_EL, | |
| 208 | desc => FOREIGN_FLOW_CONTENT_EL, | |
| 209 | title => FOREIGN_FLOW_CONTENT_EL, | |
| 210 | }, | |
| 211 | ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements. | |
| 212 | }; | |
| 213 | ||
| 214 | my $svg_attr_name = { | |
| 215 | attributetype => 'attributeType', | |
| 216 | basefrequency => 'baseFrequency', | |
| 217 | baseprofile => 'baseProfile', | |
| 218 | calcmode => 'calcMode', | |
| 219 | clippathunits => 'clipPathUnits', | |
| 220 | contentscripttype => 'contentScriptType', | |
| 221 | contentstyletype => 'contentStyleType', | |
| 222 | diffuseconstant => 'diffuseConstant', | |
| 223 | edgemode => 'edgeMode', | |
| 224 | externalresourcesrequired => 'externalResourcesRequired', | |
| 225 | fecolormatrix => 'feColorMatrix', | |
| 226 | fecomposite => 'feComposite', | |
| 227 | fegaussianblur => 'feGaussianBlur', | |
| 228 | femorphology => 'feMorphology', | |
| 229 | fetile => 'feTile', | |
| 230 | filterres => 'filterRes', | |
| 231 | filterunits => 'filterUnits', | |
| 232 | glyphref => 'glyphRef', | |
| 233 | gradienttransform => 'gradientTransform', | |
| 234 | gradientunits => 'gradientUnits', | |
| 235 | kernelmatrix => 'kernelMatrix', | |
| 236 | kernelunitlength => 'kernelUnitLength', | |
| 237 | keypoints => 'keyPoints', | |
| 238 | keysplines => 'keySplines', | |
| 239 | keytimes => 'keyTimes', | |
| 240 | lengthadjust => 'lengthAdjust', | |
| 241 | limitingconeangle => 'limitingConeAngle', | |
| 242 | markerheight => 'markerHeight', | |
| 243 | markerunits => 'markerUnits', | |
| 244 | markerwidth => 'markerWidth', | |
| 245 | maskcontentunits => 'maskContentUnits', | |
| 246 | maskunits => 'maskUnits', | |
| 247 | numoctaves => 'numOctaves', | |
| 248 | pathlength => 'pathLength', | |
| 249 | patterncontentunits => 'patternContentUnits', | |
| 250 | patterntransform => 'patternTransform', | |
| 251 | patternunits => 'patternUnits', | |
| 252 | pointsatx => 'pointsAtX', | |
| 253 | pointsaty => 'pointsAtY', | |
| 254 | pointsatz => 'pointsAtZ', | |
| 255 | preservealpha => 'preserveAlpha', | |
| 256 | preserveaspectratio => 'preserveAspectRatio', | |
| 257 | primitiveunits => 'primitiveUnits', | |
| 258 | refx => 'refX', | |
| 259 | refy => 'refY', | |
| 260 | repeatcount => 'repeatCount', | |
| 261 | repeatdur => 'repeatDur', | |
| 262 | requiredextensions => 'requiredExtensions', | |
| 263 | specularconstant => 'specularConstant', | |
| 264 | specularexponent => 'specularExponent', | |
| 265 | spreadmethod => 'spreadMethod', | |
| 266 | startoffset => 'startOffset', | |
| 267 | stddeviation => 'stdDeviation', | |
| 268 | stitchtiles => 'stitchTiles', | |
| 269 | surfacescale => 'surfaceScale', | |
| 270 | systemlanguage => 'systemLanguage', | |
| 271 | tablevalues => 'tableValues', | |
| 272 | targetx => 'targetX', | |
| 273 | targety => 'targetY', | |
| 274 | textlength => 'textLength', | |
| 275 | viewbox => 'viewBox', | |
| 276 | viewtarget => 'viewTarget', | |
| 277 | xchannelselector => 'xChannelSelector', | |
| 278 | ychannelselector => 'yChannelSelector', | |
| 279 | zoomandpan => 'zoomAndPan', | |
| 280 | }; | |
| 281 | ||
| 282 | my $foreign_attr_xname = { | |
| 283 | 'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']], | |
| 284 | 'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']], | |
| 285 | 'xlink:href' => [$XLINK_NS, ['xlink', 'href']], | |
| 286 | 'xlink:role' => [$XLINK_NS, ['xlink', 'role']], | |
| 287 | 'xlink:show' => [$XLINK_NS, ['xlink', 'show']], | |
| 288 | 'xlink:title' => [$XLINK_NS, ['xlink', 'title']], | |
| 289 | 'xlink:type' => [$XLINK_NS, ['xlink', 'type']], | |
| 290 | 'xml:base' => [$XML_NS, ['xml', 'base']], | |
| 291 | 'xml:lang' => [$XML_NS, ['xml', 'lang']], | |
| 292 | 'xml:space' => [$XML_NS, ['xml', 'space']], | |
| 293 | 'xmlns' => [$XMLNS_NS, [undef, 'xmlns']], | |
| 294 | 'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']], | |
| 295 | }; | |
| 296 | ||
| 297 | ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error. | |
| 298 | ||
| 299 | my $c1_entity_char = { | my $c1_entity_char = { |
| 300 | 0x80 => 0x20AC, | 0x80 => 0x20AC, |
| 301 | 0x81 => 0xFFFD, | 0x81 => 0xFFFD, |
| # | Line 61 my $c1_entity_char = { | Line 331 my $c1_entity_char = { |
| 331 | 0x9F => 0x0178, | 0x9F => 0x0178, |
| 332 | }; # $c1_entity_char | }; # $c1_entity_char |
| 333 | ||
| my $special_category = { | ||
| address => 1, area => 1, base => 1, basefont => 1, bgsound => 1, | ||
| blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1, | ||
| dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1, | ||
| form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1, | ||
| h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1, | ||
| img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1, | ||
| menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1, | ||
| ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1, | ||
| pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1, | ||
| textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1, | ||
| }; | ||
| my $scoping_category = { | ||
| applet => 1, button => 1, caption => 1, html => 1, marquee => 1, object => 1, | ||
| table => 1, td => 1, th => 1, | ||
| }; | ||
| my $formatting_category = { | ||
| a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1, | ||
| s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1, | ||
| }; | ||
| # $phrasing_category: all other elements | ||
| 334 | sub parse_byte_string ($$$$;$) { | sub parse_byte_string ($$$$;$) { |
| 335 | my $self = ref $_[0] ? shift : shift->new; | my $self = ref $_[0] ? shift : shift->new; |
| 336 | my $charset = shift; | my $charset_name = shift; |
| 337 | my $bytes_s = ref $_[0] ? $_[0] : \($_[0]); | open my $byte_stream, '<', ref $_[0] ? $_[0] : \($_[0]); |
| my $s; | ||
| if (defined $charset) { | ||
| require Encode; ## TODO: decode(utf8) don't delete BOM | ||
| $s = \ (Encode::decode ($charset, $$bytes_s)); | ||
| $self->{input_encoding} = lc $charset; ## TODO: normalize name | ||
| $self->{confident} = 1; | ||
| } else { | ||
| ## TODO: Implement HTML5 detection algorithm | ||
| require Whatpm::Charset::UniversalCharDet; | ||
| $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string | ||
| (substr ($$bytes_s, 0, 1024)); | ||
| $charset ||= 'windows-1252'; | ||
| $s = \ (Encode::decode ($charset, $$bytes_s)); | ||
| $self->{input_encoding} = $charset; | ||
| $self->{confident} = 0; | ||
| } | ||
| 338 | ||
| 339 | $self->{change_encoding} = sub { | my $onerror = $_[2] || sub { |
| 340 | my $self = shift; | my (%opt) = @_; |
| 341 | my $charset = lc shift; | warn "Parse error ($opt{type})\n"; |
| 342 | ## TODO: if $charset is supported | }; |
| 343 | ## TODO: normalize charset name | $self->{parse_error} = $onerror; # updated later by parse_char_string |
| 344 | ||
| 345 | ## "Change the encoding" algorithm: | ## HTML5 encoding sniffing algorithm |
| 346 | require Message::Charset::Info; | |
| 347 | ## Step 1 | my $charset; |
| 348 | if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8? | my $buffer; |
| 349 | $charset = 'utf-8'; | my ($char_stream, $e_status); |
| 350 | ||
| 351 | SNIFFING: { | |
| 352 | ||
| 353 | ## Step 1 | |
| 354 | if (defined $charset_name) { | |
| 355 | $charset = Message::Charset::Info->get_by_iana_name ($charset_name); | |
| 356 | ||
| 357 | ## ISSUE: Unsupported encoding is not ignored according to the spec. | |
| 358 | ($char_stream, $e_status) = $charset->get_decode_handle | |
| 359 | ($byte_stream, allow_error_reporting => 1, | |
| 360 | allow_fallback => 1); | |
| 361 | if ($char_stream) { | |
| 362 | $self->{confident} = 1; | |
| 363 | last SNIFFING; | |
| 364 | } else { | |
| 365 | ## TODO: unsupported error | |
| 366 | } | |
| 367 | } | } |
| 368 | ||
| 369 | ## Step 2 | ## Step 2 |
| 370 | if (defined $self->{input_encoding} and | my $byte_buffer = ''; |
| 371 | $self->{input_encoding} eq $charset) { | for (1..1024) { |
| 372 | my $char = $byte_stream->getc; | |
| 373 | last unless defined $char; | |
| 374 | $byte_buffer .= $char; | |
| 375 | } ## TODO: timeout | |
| 376 | ||
| 377 | ## Step 3 | |
| 378 | if ($byte_buffer =~ /^\xFE\xFF/) { | |
| 379 | $charset = Message::Charset::Info->get_by_iana_name ('utf-16be'); | |
| 380 | ($char_stream, $e_status) = $charset->get_decode_handle | |
| 381 | ($byte_stream, allow_error_reporting => 1, | |
| 382 | allow_fallback => 1, byte_buffer => \$byte_buffer); | |
| 383 | $self->{confident} = 1; | $self->{confident} = 1; |
| 384 | return; | last SNIFFING; |
| 385 | } elsif ($byte_buffer =~ /^\xFF\xFE/) { | |
| 386 | $charset = Message::Charset::Info->get_by_iana_name ('utf-16le'); | |
| 387 | ($char_stream, $e_status) = $charset->get_decode_handle | |
| 388 | ($byte_stream, allow_error_reporting => 1, | |
| 389 | allow_fallback => 1, byte_buffer => \$byte_buffer); | |
| 390 | $self->{confident} = 1; | |
| 391 | last SNIFFING; | |
| 392 | } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) { | |
| 393 | $charset = Message::Charset::Info->get_by_iana_name ('utf-8'); | |
| 394 | ($char_stream, $e_status) = $charset->get_decode_handle | |
| 395 | ($byte_stream, allow_error_reporting => 1, | |
| 396 | allow_fallback => 1, byte_buffer => \$byte_buffer); | |
| 397 | $self->{confident} = 1; | |
| 398 | last SNIFFING; | |
| 399 | } | } |
| 400 | ||
| 401 | !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}. | ## Step 4 |
| 402 | ':'.$charset, level => 'w'); | ## TODO: <meta charset> |
| 403 | ||
| 404 | ## Step 3 | ## Step 5 |
| 405 | # if (can) { | ## TODO: from history |
| ## change the encoding on the fly. | ||
| #$self->{confident} = 1; | ||
| #return; | ||
| # } | ||
| 406 | ||
| 407 | ## Step 4 | ## Step 6 |
| 408 | throw Whatpm::HTML::RestartParser (charset => $charset); | require Whatpm::Charset::UniversalCharDet; |
| 409 | $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string | |
| 410 | ($byte_buffer); | |
| 411 | if (defined $charset_name) { | |
| 412 | $charset = Message::Charset::Info->get_by_iana_name ($charset_name); | |
| 413 | ||
| 414 | ## ISSUE: Unsupported encoding is not ignored according to the spec. | |
| 415 | require Whatpm::Charset::DecodeHandle; | |
| 416 | $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new | |
| 417 | ($byte_stream); | |
| 418 | ($char_stream, $e_status) = $charset->get_decode_handle | |
| 419 | ($buffer, allow_error_reporting => 1, | |
| 420 | allow_fallback => 1, byte_buffer => \$byte_buffer); | |
| 421 | if ($char_stream) { | |
| 422 | $buffer->{buffer} = $byte_buffer; | |
| 423 | !!!parse-error (type => 'sniffing:chardet', ## TODO: type name | |
| 424 | value => $charset_name, | |
| 425 | level => $self->{info_level}, | |
| 426 | line => 1, column => 1); | |
| 427 | $self->{confident} = 0; | |
| 428 | last SNIFFING; | |
| 429 | } | |
| 430 | } | |
| 431 | ||
| 432 | ## Step 7: default | |
| 433 | ## TODO: Make this configurable. | |
| 434 | $charset = Message::Charset::Info->get_by_iana_name ('windows-1252'); | |
| 435 | ## NOTE: We choose |windows-1252| here, since |utf-8| should be | |
| 436 | ## detectable in the step 6. | |
| 437 | require Whatpm::Charset::DecodeHandle; | |
| 438 | $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new | |
| 439 | ($byte_stream); | |
| 440 | ($char_stream, $e_status) | |
| 441 | = $charset->get_decode_handle ($buffer, | |
| 442 | allow_error_reporting => 1, | |
| 443 | allow_fallback => 1, | |
| 444 | byte_buffer => \$byte_buffer); | |
| 445 | $buffer->{buffer} = $byte_buffer; | |
| 446 | !!!parse-error (type => 'sniffing:default', ## TODO: type name | |
| 447 | value => 'windows-1252', | |
| 448 | level => $self->{info_level}, | |
| 449 | line => 1, column => 1); | |
| 450 | $self->{confident} = 0; | |
| 451 | } # SNIFFING | |
| 452 | ||
| 453 | $self->{input_encoding} = $charset->get_iana_name; | |
| 454 | if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { | |
| 455 | !!!parse-error (type => 'chardecode:fallback', ## TODO: type name | |
| 456 | value => $self->{input_encoding}, | |
| 457 | level => $self->{unsupported_level}, | |
| 458 | line => 1, column => 1); | |
| 459 | } elsif (not ($e_status & | |
| 460 | Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) { | |
| 461 | !!!parse-error (type => 'chardecode:no error', ## TODO: type name | |
| 462 | value => $self->{input_encoding}, | |
| 463 | level => $self->{unsupported_level}, | |
| 464 | line => 1, column => 1); | |
| 465 | } | |
| 466 | ||
| 467 | $self->{change_encoding} = sub { | |
| 468 | my $self = shift; | |
| 469 | $charset_name = shift; | |
| 470 | my $token = shift; | |
| 471 | ||
| 472 | $charset = Message::Charset::Info->get_by_iana_name ($charset_name); | |
| 473 | ($char_stream, $e_status) = $charset->get_decode_handle | |
| 474 | ($byte_stream, allow_error_reporting => 1, allow_fallback => 1, | |
| 475 | byte_buffer => \ $buffer->{buffer}); | |
| 476 | ||
| 477 | if ($char_stream) { # if supported | |
| 478 | ## "Change the encoding" algorithm: | |
| 479 | ||
| 480 | ## Step 1 | |
| 481 | if ($charset->{iana_names}->{'utf-16'}) { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8? | |
| 482 | $charset = Message::Charset::Info->get_by_iana_name ('utf-8'); | |
| 483 | ($char_stream, $e_status) = $charset->get_decode_handle | |
| 484 | ($byte_stream, | |
| 485 | byte_buffer => \ $buffer->{buffer}); | |
| 486 | } | |
| 487 | $charset_name = $charset->get_iana_name; | |
| 488 | ||
| 489 | ## Step 2 | |
| 490 | if (defined $self->{input_encoding} and | |
| 491 | $self->{input_encoding} eq $charset_name) { | |
| 492 | !!!parse-error (type => 'charset label:matching', ## TODO: type | |
| 493 | value => $charset_name, | |
| 494 | level => $self->{info_level}); | |
| 495 | $self->{confident} = 1; | |
| 496 | return; | |
| 497 | } | |
| 498 | ||
| 499 | !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}. | |
| 500 | ':'.$charset_name, level => 'w', token => $token); | |
| 501 | ||
| 502 | ## Step 3 | |
| 503 | # if (can) { | |
| 504 | ## change the encoding on the fly. | |
| 505 | #$self->{confident} = 1; | |
| 506 | #return; | |
| 507 | # } | |
| 508 | ||
| 509 | ## Step 4 | |
| 510 | throw Whatpm::HTML::RestartParser (); | |
| 511 | } | |
| 512 | }; # $self->{change_encoding} | }; # $self->{change_encoding} |
| 513 | ||
| 514 | my $char_onerror = sub { | |
| 515 | my (undef, $type, %opt) = @_; | |
| 516 | !!!parse-error (%opt, type => $type); | |
| 517 | if ($opt{octets}) { | |
| 518 | ${$opt{octets}} = "\x{FFFD}"; # relacement character | |
| 519 | } | |
| 520 | }; | |
| 521 | $char_stream->onerror ($char_onerror); | |
| 522 | ||
| 523 | my @args = @_; shift @args; # $s | my @args = @_; shift @args; # $s |
| 524 | my $return; | my $return; |
| 525 | try { | try { |
| 526 | $return = $self->parse_char_string ($s, @args); | $return = $self->parse_char_stream ($char_stream, @args); |
| 527 | } catch Whatpm::HTML::RestartParser with { | } catch Whatpm::HTML::RestartParser with { |
| 528 | my $charset = shift->{charset}; | ## NOTE: Invoked after {change_encoding}. |
| 529 | $s = \ (Encode::decode ($charset, $$bytes_s)); | |
| 530 | $self->{input_encoding} = $charset; ## TODO: normalize | $self->{input_encoding} = $charset->get_iana_name; |
| 531 | if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { | |
| 532 | !!!parse-error (type => 'chardecode:fallback', ## TODO: type name | |
| 533 | value => $self->{input_encoding}, | |
| 534 | level => $self->{unsupported_level}, | |
| 535 | line => 1, column => 1); | |
| 536 | } elsif (not ($e_status & | |
| 537 | Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) { | |
| 538 | !!!parse-error (type => 'chardecode:no error', ## TODO: type name | |
| 539 | value => $self->{input_encoding}, | |
| 540 | level => $self->{unsupported_level}, | |
| 541 | line => 1, column => 1); | |
| 542 | } | |
| 543 | $self->{confident} = 1; | $self->{confident} = 1; |
| 544 | $return = $self->parse_char_string ($s, @args); | $char_stream->onerror ($char_onerror); |
| 545 | $return = $self->parse_char_stream ($char_stream, @args); | |
| 546 | }; | }; |
| 547 | return $return; | return $return; |
| 548 | } # parse_byte_string | } # parse_byte_string |
| # | Line 162 sub parse_byte_string ($$$$;$) { | Line 556 sub parse_byte_string ($$$$;$) { |
| 556 | ## such as |parse_byte_string| in this module, must ensure that it does | ## such as |parse_byte_string| in this module, must ensure that it does |
| 557 | ## strip the BOM and never strip any ZWNBSP. | ## strip the BOM and never strip any ZWNBSP. |
| 558 | ||
| 559 | *parse_char_string = \&parse_string; | sub parse_char_string ($$$;$) { |
| 560 | my $self = shift; | |
| 561 | open my $input, '<:utf8', ref $_[0] ? $_[0] : \($_[0]); | |
| 562 | return $self->parse_char_stream ($input, @_[1..$#_]); | |
| 563 | } # parse_char_string | |
| 564 | *parse_string = \&parse_char_string; | |
| 565 | ||
| 566 | sub parse_string ($$$;$) { | sub parse_char_stream ($$$;$) { |
| 567 | my $self = ref $_[0] ? shift : shift->new; | my $self = ref $_[0] ? shift : shift->new; |
| 568 | my $s = ref $_[0] ? $_[0] : \($_[0]); | my $input = $_[0]; |
| 569 | $self->{document} = $_[1]; | $self->{document} = $_[1]; |
| 570 | @{$self->{document}->child_nodes} = (); | @{$self->{document}->child_nodes} = (); |
| 571 | ||
| # | Line 177 sub parse_string ($$$;$) { | Line 576 sub parse_string ($$$;$) { |
| 576 | if defined $self->{input_encoding}; | if defined $self->{input_encoding}; |
| 577 | ||
| 578 | my $i = 0; | my $i = 0; |
| 579 | my $line = 1; | $self->{line_prev} = $self->{line} = 1; |
| 580 | my $column = 0; | $self->{column_prev} = $self->{column} = 0; |
| 581 | $self->{set_next_char} = sub { | $self->{set_next_char} = sub { |
| 582 | my $self = shift; | my $self = shift; |
| 583 | ||
| 584 | pop @{$self->{prev_char}}; | pop @{$self->{prev_char}}; |
| 585 | unshift @{$self->{prev_char}}, $self->{next_char}; | unshift @{$self->{prev_char}}, $self->{next_char}; |
| 586 | ||
| 587 | $self->{next_char} = -1 and return if $i >= length $$s; | my $char = $input->getc; |
| 588 | $self->{next_char} = ord substr $$s, $i++, 1; | $self->{next_char} = -1 and return unless defined $char; |
| 589 | $column++; | $self->{next_char} = ord $char; |
| 590 | ||
| 591 | ($self->{line_prev}, $self->{column_prev}) | |
| 592 | = ($self->{line}, $self->{column}); | |
| 593 | $self->{column}++; | |
| 594 | ||
| 595 | if ($self->{next_char} == 0x000A) { # LF | if ($self->{next_char} == 0x000A) { # LF |
| 596 | $line++; | !!!cp ('j1'); |
| 597 | $column = 0; | $self->{line}++; |
| 598 | $self->{column} = 0; | |
| 599 | } elsif ($self->{next_char} == 0x000D) { # CR | } elsif ($self->{next_char} == 0x000D) { # CR |
| 600 | $i++ if substr ($$s, $i, 1) eq "\x0A"; | !!!cp ('j2'); |
| 601 | my $next = $input->getc; | |
| 602 | if ($next ne "\x0A") { | |
| 603 | $input->ungetc ($next); | |
| 604 | } | |
| 605 | $self->{next_char} = 0x000A; # LF # MUST | $self->{next_char} = 0x000A; # LF # MUST |
| 606 | $line++; | $self->{line}++; |
| 607 | $column = 0; | $self->{column} = 0; |
| 608 | } elsif ($self->{next_char} > 0x10FFFF) { | } elsif ($self->{next_char} > 0x10FFFF) { |
| 609 | !!!cp ('j3'); | |
| 610 | $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST | $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST |
| 611 | } elsif ($self->{next_char} == 0x0000) { # NULL | } elsif ($self->{next_char} == 0x0000) { # NULL |
| 612 | !!!cp ('j4'); | |
| 613 | !!!parse-error (type => 'NULL'); | !!!parse-error (type => 'NULL'); |
| 614 | $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST | $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST |
| 615 | } elsif ($self->{next_char} <= 0x0008 or | |
| 616 | (0x000E <= $self->{next_char} and $self->{next_char} <= 0x001F) or | |
| 617 | (0x007F <= $self->{next_char} and $self->{next_char} <= 0x009F) or | |
| 618 | (0xD800 <= $self->{next_char} and $self->{next_char} <= 0xDFFF) or | |
| 619 | (0xFDD0 <= $self->{next_char} and $self->{next_char} <= 0xFDDF) or | |
| 620 | { | |
| 621 | 0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1, | |
| 622 | 0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1, | |
| 623 | 0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1, | |
| 624 | 0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1, | |
| 625 | 0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1, | |
| 626 | 0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1, | |
| 627 | 0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1, | |
| 628 | 0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1, | |
| 629 | 0x10FFFE => 1, 0x10FFFF => 1, | |
| 630 | }->{$self->{next_char}}) { | |
| 631 | !!!cp ('j5'); | |
| 632 | !!!parse-error (type => 'control char', level => $self->{must_level}); | |
| 633 | ## TODO: error type documentation | |
| 634 | } | } |
| 635 | }; | }; |
| 636 | $self->{prev_char} = [-1, -1, -1]; | $self->{prev_char} = [-1, -1, -1]; |
| # | Line 209 sub parse_string ($$$;$) { | Line 638 sub parse_string ($$$;$) { |
| 638 | ||
| 639 | my $onerror = $_[2] || sub { | my $onerror = $_[2] || sub { |
| 640 | my (%opt) = @_; | my (%opt) = @_; |
| 641 | warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n"; | my $line = $opt{token} ? $opt{token}->{line} : $opt{line}; |
| 642 | my $column = $opt{token} ? $opt{token}->{column} : $opt{column}; | |
| 643 | warn "Parse error ($opt{type}) at line $line column $column\n"; | |
| 644 | }; | }; |
| 645 | $self->{parse_error} = sub { | $self->{parse_error} = sub { |
| 646 | $onerror->(@_, line => $line, column => $column); | $onerror->(line => $self->{line}, column => $self->{column}, @_); |
| 647 | }; | }; |
| 648 | ||
| 649 | $self->_initialize_tokenizer; | $self->_initialize_tokenizer; |
| # | Line 220 sub parse_string ($$$;$) { | Line 651 sub parse_string ($$$;$) { |
| 651 | $self->_construct_tree; | $self->_construct_tree; |
| 652 | $self->_terminate_tree_constructor; | $self->_terminate_tree_constructor; |
| 653 | ||
| 654 | delete $self->{parse_error}; # remove loop | |
| 655 | ||
| 656 | return $self->{document}; | return $self->{document}; |
| 657 | } # parse_string | } # parse_char_stream |
| 658 | ||
| 659 | sub new ($) { | sub new ($) { |
| 660 | my $class = shift; | my $class = shift; |
| 661 | my $self = bless {}, $class; | my $self = bless { |
| 662 | must_level => 'm', | |
| 663 | should_level => 's', | |
| 664 | good_level => 'w', | |
| 665 | warn_level => 'w', | |
| 666 | info_level => 'i', | |
| 667 | unsupported_level => 'u', | |
| 668 | }, $class; | |
| 669 | $self->{set_next_char} = sub { | $self->{set_next_char} = sub { |
| 670 | $self->{next_char} = -1; | $self->{next_char} = -1; |
| 671 | }; | }; |
| # | Line 287 sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO | Line 727 sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO |
| 727 | sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 } | sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 } |
| 728 | sub BOGUS_DOCTYPE_STATE () { 32 } | sub BOGUS_DOCTYPE_STATE () { 32 } |
| 729 | sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 } | sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 } |
| 730 | sub SELF_CLOSING_START_TAG_STATE () { 34 } | |
| 731 | sub CDATA_BLOCK_STATE () { 35 } | |
| 732 | ||
| 733 | sub DOCTYPE_TOKEN () { 1 } | sub DOCTYPE_TOKEN () { 1 } |
| 734 | sub COMMENT_TOKEN () { 2 } | sub COMMENT_TOKEN () { 2 } |
| # | Line 304 sub ROW_IMS () { 0b10000000 } | Line 746 sub ROW_IMS () { 0b10000000 } |
| 746 | sub BODY_AFTER_IMS () { 0b100000000 } | sub BODY_AFTER_IMS () { 0b100000000 } |
| 747 | sub FRAME_IMS () { 0b1000000000 } | sub FRAME_IMS () { 0b1000000000 } |
| 748 | sub SELECT_IMS () { 0b10000000000 } | sub SELECT_IMS () { 0b10000000000 } |
| 749 | sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 } | |
| 750 | ## NOTE: "in foreign content" insertion mode is special; it is combined | |
| 751 | ## with the secondary insertion mode. In this parser, they are stored | |
| 752 | ## together in the bit-or'ed form. | |
| 753 | ||
| 754 | ## NOTE: "initial" and "before html" insertion modes have no constants. | ## NOTE: "initial" and "before html" insertion modes have no constants. |
| 755 | ||
| # | Line 340 sub _initialize_tokenizer ($) { | Line 786 sub _initialize_tokenizer ($) { |
| 786 | undef $self->{current_attribute}; | undef $self->{current_attribute}; |
| 787 | undef $self->{last_emitted_start_tag_name}; | undef $self->{last_emitted_start_tag_name}; |
| 788 | undef $self->{last_attribute_value_state}; | undef $self->{last_attribute_value_state}; |
| 789 | delete $self->{self_closing}; | |
| 790 | $self->{char} = []; | $self->{char} = []; |
| 791 | # $self->{next_char} | # $self->{next_char} |
| 792 | !!!next-input-character; | !!!next-input-character; |
| # | Line 360 sub _initialize_tokenizer ($) { | Line 807 sub _initialize_tokenizer ($) { |
| 807 | ## ->{value} | ## ->{value} |
| 808 | ## ->{has_reference} == 1 or 0 | ## ->{has_reference} == 1 or 0 |
| 809 | ## ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN) | ## ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN) |
| 810 | ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|. | |
| 811 | ## |->{self_closing}| is used to save the value of |$self->{self_closing}| | |
| 812 | ## while the token is pushed back to the stack. | |
| 813 | ||
| 814 | ## ISSUE: "When a DOCTYPE token is created, its | |
| 815 | ## <i>self-closing flag</i> must be unset (its other state is that it | |
| 816 | ## be set), and its attributes list must be empty.": Wrong subject? | |
| 817 | ||
| 818 | ## Emitted token MUST immediately be handled by the tree construction state. | ## Emitted token MUST immediately be handled by the tree construction state. |
| 819 | ||
| # | Line 386 sub _initialize_tokenizer ($) { | Line 840 sub _initialize_tokenizer ($) { |
| 840 | ||
| 841 | sub _get_next_token ($) { | sub _get_next_token ($) { |
| 842 | my $self = shift; | my $self = shift; |
| 843 | ||
| 844 | if ($self->{self_closing}) { | |
| 845 | !!!parse-error (type => 'nestc', token => $self->{current_token}); | |
| 846 | ## NOTE: The |self_closing| flag is only set by start tag token. | |
| 847 | ## In addition, when a start tag token is emitted, it is always set to | |
| 848 | ## |current_token|. | |
| 849 | delete $self->{self_closing}; | |
| 850 | } | |
| 851 | ||
| 852 | if (@{$self->{token}}) { | if (@{$self->{token}}) { |
| 853 | $self->{self_closing} = $self->{token}->[0]->{self_closing}; | |
| 854 | return shift @{$self->{token}}; | return shift @{$self->{token}}; |
| 855 | } | } |
| 856 | ||
| # | Line 449 sub _get_next_token ($) { | Line 913 sub _get_next_token ($) { |
| 913 | # | # |
| 914 | } elsif ($self->{next_char} == -1) { | } elsif ($self->{next_char} == -1) { |
| 915 | !!!cp (11); | !!!cp (11); |
| 916 | !!!emit ({type => END_OF_FILE_TOKEN}); | !!!emit ({type => END_OF_FILE_TOKEN, |
| 917 | line => $self->{line}, column => $self->{column}}); | |
| 918 | last A; ## TODO: ok? | last A; ## TODO: ok? |
| 919 | } else { | } else { |
| 920 | !!!cp (12); | !!!cp (12); |
| 921 | } | } |
| 922 | # Anything else | # Anything else |
| 923 | my $token = {type => CHARACTER_TOKEN, | my $token = {type => CHARACTER_TOKEN, |
| 924 | data => chr $self->{next_char}}; | data => chr $self->{next_char}, |
| 925 | line => $self->{line}, column => $self->{column}, | |
| 926 | }; | |
| 927 | ## Stay in the data state | ## Stay in the data state |
| 928 | !!!next-input-character; | !!!next-input-character; |
| 929 | ||
| # | Line 465 sub _get_next_token ($) { | Line 932 sub _get_next_token ($) { |
| 932 | redo A; | redo A; |
| 933 | } elsif ($self->{state} == ENTITY_DATA_STATE) { | } elsif ($self->{state} == ENTITY_DATA_STATE) { |
| 934 | ## (cannot happen in CDATA state) | ## (cannot happen in CDATA state) |
| 935 | ||
| 936 | my ($l, $c) = ($self->{line_prev}, $self->{column_prev}); | |
| 937 | ||
| 938 | my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1); | my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1); |
| 939 | ||
| # | Line 473 sub _get_next_token ($) { | Line 942 sub _get_next_token ($) { |
| 942 | ||
| 943 | unless (defined $token) { | unless (defined $token) { |
| 944 | !!!cp (13); | !!!cp (13); |
| 945 | !!!emit ({type => CHARACTER_TOKEN, data => '&'}); | !!!emit ({type => CHARACTER_TOKEN, data => '&', |
| 946 | line => $l, column => $c, | |
| 947 | }); | |
| 948 | } else { | } else { |
| 949 | !!!cp (14); | !!!cp (14); |
| 950 | !!!emit ($token); | !!!emit ($token); |
| # | Line 492 sub _get_next_token ($) { | Line 963 sub _get_next_token ($) { |
| 963 | ## reconsume | ## reconsume |
| 964 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 965 | ||
| 966 | !!!emit ({type => CHARACTER_TOKEN, data => '<'}); | !!!emit ({type => CHARACTER_TOKEN, data => '<', |
| 967 | line => $self->{line_prev}, | |
| 968 | column => $self->{column_prev}, | |
| 969 | }); | |
| 970 | ||
| 971 | redo A; | redo A; |
| 972 | } | } |
| # | Line 512 sub _get_next_token ($) { | Line 986 sub _get_next_token ($) { |
| 986 | !!!cp (19); | !!!cp (19); |
| 987 | $self->{current_token} | $self->{current_token} |
| 988 | = {type => START_TAG_TOKEN, | = {type => START_TAG_TOKEN, |
| 989 | tag_name => chr ($self->{next_char} + 0x0020)}; | tag_name => chr ($self->{next_char} + 0x0020), |
| 990 | line => $self->{line_prev}, | |
| 991 | column => $self->{column_prev}}; | |
| 992 | $self->{state} = TAG_NAME_STATE; | $self->{state} = TAG_NAME_STATE; |
| 993 | !!!next-input-character; | !!!next-input-character; |
| 994 | redo A; | redo A; |
| # | Line 520 sub _get_next_token ($) { | Line 996 sub _get_next_token ($) { |
| 996 | $self->{next_char} <= 0x007A) { # a..z | $self->{next_char} <= 0x007A) { # a..z |
| 997 | !!!cp (20); | !!!cp (20); |
| 998 | $self->{current_token} = {type => START_TAG_TOKEN, | $self->{current_token} = {type => START_TAG_TOKEN, |
| 999 | tag_name => chr ($self->{next_char})}; | tag_name => chr ($self->{next_char}), |
| 1000 | line => $self->{line_prev}, | |
| 1001 | column => $self->{column_prev}}; | |
| 1002 | $self->{state} = TAG_NAME_STATE; | $self->{state} = TAG_NAME_STATE; |
| 1003 | !!!next-input-character; | !!!next-input-character; |
| 1004 | redo A; | redo A; |
| 1005 | } elsif ($self->{next_char} == 0x003E) { # > | } elsif ($self->{next_char} == 0x003E) { # > |
| 1006 | !!!cp (21); | !!!cp (21); |
| 1007 | !!!parse-error (type => 'empty start tag'); | !!!parse-error (type => 'empty start tag', |
| 1008 | line => $self->{line_prev}, | |
| 1009 | column => $self->{column_prev}); | |
| 1010 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1011 | !!!next-input-character; | !!!next-input-character; |
| 1012 | ||
| 1013 | !!!emit ({type => CHARACTER_TOKEN, data => '<>'}); | !!!emit ({type => CHARACTER_TOKEN, data => '<>', |
| 1014 | line => $self->{line_prev}, | |
| 1015 | column => $self->{column_prev}, | |
| 1016 | }); | |
| 1017 | ||
| 1018 | redo A; | redo A; |
| 1019 | } elsif ($self->{next_char} == 0x003F) { # ? | } elsif ($self->{next_char} == 0x003F) { # ? |
| 1020 | !!!cp (22); | !!!cp (22); |
| 1021 | !!!parse-error (type => 'pio'); | !!!parse-error (type => 'pio', |
| 1022 | line => $self->{line_prev}, | |
| 1023 | column => $self->{column_prev}); | |
| 1024 | $self->{state} = BOGUS_COMMENT_STATE; | $self->{state} = BOGUS_COMMENT_STATE; |
| 1025 | $self->{current_token} = {type => COMMENT_TOKEN, data => '', | |
| 1026 | line => $self->{line_prev}, | |
| 1027 | column => $self->{column_prev}, | |
| 1028 | }; | |
| 1029 | ## $self->{next_char} is intentionally left as is | ## $self->{next_char} is intentionally left as is |
| 1030 | redo A; | redo A; |
| 1031 | } else { | } else { |
| 1032 | !!!cp (23); | !!!cp (23); |
| 1033 | !!!parse-error (type => 'bare stago'); | !!!parse-error (type => 'bare stago', |
| 1034 | line => $self->{line_prev}, | |
| 1035 | column => $self->{column_prev}); | |
| 1036 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1037 | ## reconsume | ## reconsume |
| 1038 | ||
| 1039 | !!!emit ({type => CHARACTER_TOKEN, data => '<'}); | !!!emit ({type => CHARACTER_TOKEN, data => '<', |
| 1040 | line => $self->{line_prev}, | |
| 1041 | column => $self->{column_prev}, | |
| 1042 | }); | |
| 1043 | ||
| 1044 | redo A; | redo A; |
| 1045 | } | } |
| # | Line 553 sub _get_next_token ($) { | Line 1047 sub _get_next_token ($) { |
| 1047 | die "$0: $self->{content_model} in tag open"; | die "$0: $self->{content_model} in tag open"; |
| 1048 | } | } |
| 1049 | } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) { | } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) { |
| 1050 | my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</" | |
| 1051 | if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA | if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA |
| 1052 | if (defined $self->{last_emitted_start_tag_name}) { | if (defined $self->{last_emitted_start_tag_name}) { |
| 1053 | ||
| 1054 | ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564> | ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564> |
| 1055 | my @next_char; | my @next_char; |
| 1056 | TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) { | TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) { |
| # | Line 571 sub _get_next_token ($) { | Line 1067 sub _get_next_token ($) { |
| 1067 | !!!back-next-input-character (@next_char); | !!!back-next-input-character (@next_char); |
| 1068 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1069 | ||
| 1070 | !!!emit ({type => CHARACTER_TOKEN, data => '</'}); | !!!emit ({type => CHARACTER_TOKEN, data => '</', |
| 1071 | line => $l, column => $c, | |
| 1072 | }); | |
| 1073 | ||
| 1074 | redo A; | redo A; |
| 1075 | } | } |
| # | Line 590 sub _get_next_token ($) { | Line 1088 sub _get_next_token ($) { |
| 1088 | $self->{next_char} = shift @next_char; # reconsume | $self->{next_char} = shift @next_char; # reconsume |
| 1089 | !!!back-next-input-character (@next_char); | !!!back-next-input-character (@next_char); |
| 1090 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1091 | !!!emit ({type => CHARACTER_TOKEN, data => '</'}); | !!!emit ({type => CHARACTER_TOKEN, data => '</', |
| 1092 | line => $l, column => $c, | |
| 1093 | }); | |
| 1094 | redo A; | redo A; |
| 1095 | } else { | } else { |
| 1096 | !!!cp (27); | !!!cp (27); |
| # | Line 603 sub _get_next_token ($) { | Line 1103 sub _get_next_token ($) { |
| 1103 | !!!cp (28); | !!!cp (28); |
| 1104 | # next-input-character is already done | # next-input-character is already done |
| 1105 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1106 | !!!emit ({type => CHARACTER_TOKEN, data => '</'}); | !!!emit ({type => CHARACTER_TOKEN, data => '</', |
| 1107 | line => $l, column => $c, | |
| 1108 | }); | |
| 1109 | redo A; | redo A; |
| 1110 | } | } |
| 1111 | } | } |
| # | Line 611 sub _get_next_token ($) { | Line 1113 sub _get_next_token ($) { |
| 1113 | if (0x0041 <= $self->{next_char} and | if (0x0041 <= $self->{next_char} and |
| 1114 | $self->{next_char} <= 0x005A) { # A..Z | $self->{next_char} <= 0x005A) { # A..Z |
| 1115 | !!!cp (29); | !!!cp (29); |
| 1116 | $self->{current_token} = {type => END_TAG_TOKEN, | $self->{current_token} |
| 1117 | tag_name => chr ($self->{next_char} + 0x0020)}; | = {type => END_TAG_TOKEN, |
| 1118 | tag_name => chr ($self->{next_char} + 0x0020), | |
| 1119 | line => $l, column => $c}; | |
| 1120 | $self->{state} = TAG_NAME_STATE; | $self->{state} = TAG_NAME_STATE; |
| 1121 | !!!next-input-character; | !!!next-input-character; |
| 1122 | redo A; | redo A; |
| # | Line 620 sub _get_next_token ($) { | Line 1124 sub _get_next_token ($) { |
| 1124 | $self->{next_char} <= 0x007A) { # a..z | $self->{next_char} <= 0x007A) { # a..z |
| 1125 | !!!cp (30); | !!!cp (30); |
| 1126 | $self->{current_token} = {type => END_TAG_TOKEN, | $self->{current_token} = {type => END_TAG_TOKEN, |
| 1127 | tag_name => chr ($self->{next_char})}; | tag_name => chr ($self->{next_char}), |
| 1128 | line => $l, column => $c}; | |
| 1129 | $self->{state} = TAG_NAME_STATE; | $self->{state} = TAG_NAME_STATE; |
| 1130 | !!!next-input-character; | !!!next-input-character; |
| 1131 | redo A; | redo A; |
| 1132 | } elsif ($self->{next_char} == 0x003E) { # > | } elsif ($self->{next_char} == 0x003E) { # > |
| 1133 | !!!cp (31); | !!!cp (31); |
| 1134 | !!!parse-error (type => 'empty end tag'); | !!!parse-error (type => 'empty end tag', |
| 1135 | line => $self->{line_prev}, ## "<" in "</>" | |
| 1136 | column => $self->{column_prev} - 1); | |
| 1137 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1138 | !!!next-input-character; | !!!next-input-character; |
| 1139 | redo A; | redo A; |
| # | Line 636 sub _get_next_token ($) { | Line 1143 sub _get_next_token ($) { |
| 1143 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1144 | # reconsume | # reconsume |
| 1145 | ||
| 1146 | !!!emit ({type => CHARACTER_TOKEN, data => '</'}); | !!!emit ({type => CHARACTER_TOKEN, data => '</', |
| 1147 | line => $l, column => $c, | |
| 1148 | }); | |
| 1149 | ||
| 1150 | redo A; | redo A; |
| 1151 | } else { | } else { |
| 1152 | !!!cp (33); | !!!cp (33); |
| 1153 | !!!parse-error (type => 'bogus end tag'); | !!!parse-error (type => 'bogus end tag'); |
| 1154 | $self->{state} = BOGUS_COMMENT_STATE; | $self->{state} = BOGUS_COMMENT_STATE; |
| 1155 | $self->{current_token} = {type => COMMENT_TOKEN, data => '', | |
| 1156 | line => $self->{line_prev}, # "<" of "</" | |
| 1157 | column => $self->{column_prev} - 1, | |
| 1158 | }; | |
| 1159 | ## $self->{next_char} is intentionally left as is | ## $self->{next_char} is intentionally left as is |
| 1160 | redo A; | redo A; |
| 1161 | } | } |
| # | Line 659 sub _get_next_token ($) { | Line 1172 sub _get_next_token ($) { |
| 1172 | } elsif ($self->{next_char} == 0x003E) { # > | } elsif ($self->{next_char} == 0x003E) { # > |
| 1173 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1174 | !!!cp (35); | !!!cp (35); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1175 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1176 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1177 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 692 sub _get_next_token ($) { | Line 1203 sub _get_next_token ($) { |
| 1203 | !!!parse-error (type => 'unclosed tag'); | !!!parse-error (type => 'unclosed tag'); |
| 1204 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1205 | !!!cp (39); | !!!cp (39); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1206 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1207 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1208 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 714 sub _get_next_token ($) { | Line 1223 sub _get_next_token ($) { |
| 1223 | ||
| 1224 | redo A; | redo A; |
| 1225 | } elsif ($self->{next_char} == 0x002F) { # / | } elsif ($self->{next_char} == 0x002F) { # / |
| 1226 | !!!cp (42); | |
| 1227 | $self->{state} = SELF_CLOSING_START_TAG_STATE; | |
| 1228 | !!!next-input-character; | !!!next-input-character; |
| if ($self->{next_char} == 0x003E and # > | ||
| $self->{current_token}->{type} == START_TAG_TOKEN and | ||
| $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) { | ||
| # permitted slash | ||
| !!!cp (42); | ||
| # | ||
| } else { | ||
| !!!cp (43); | ||
| !!!parse-error (type => 'nestc'); | ||
| } | ||
| $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE; | ||
| # next-input-character is already done | ||
| 1229 | redo A; | redo A; |
| 1230 | } else { | } else { |
| 1231 | !!!cp (44); | !!!cp (44); |
| # | Line 749 sub _get_next_token ($) { | Line 1248 sub _get_next_token ($) { |
| 1248 | } elsif ($self->{next_char} == 0x003E) { # > | } elsif ($self->{next_char} == 0x003E) { # > |
| 1249 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1250 | !!!cp (46); | !!!cp (46); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1251 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1252 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1253 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 772 sub _get_next_token ($) { | Line 1269 sub _get_next_token ($) { |
| 1269 | } elsif (0x0041 <= $self->{next_char} and | } elsif (0x0041 <= $self->{next_char} and |
| 1270 | $self->{next_char} <= 0x005A) { # A..Z | $self->{next_char} <= 0x005A) { # A..Z |
| 1271 | !!!cp (49); | !!!cp (49); |
| 1272 | $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020), | $self->{current_attribute} |
| 1273 | value => ''}; | = {name => chr ($self->{next_char} + 0x0020), |
| 1274 | value => '', | |
| 1275 | line => $self->{line}, column => $self->{column}}; | |
| 1276 | $self->{state} = ATTRIBUTE_NAME_STATE; | $self->{state} = ATTRIBUTE_NAME_STATE; |
| 1277 | !!!next-input-character; | !!!next-input-character; |
| 1278 | redo A; | redo A; |
| 1279 | } elsif ($self->{next_char} == 0x002F) { # / | } elsif ($self->{next_char} == 0x002F) { # / |
| 1280 | !!!cp (50); | |
| 1281 | $self->{state} = SELF_CLOSING_START_TAG_STATE; | |
| 1282 | !!!next-input-character; | !!!next-input-character; |
| if ($self->{next_char} == 0x003E and # > | ||
| $self->{current_token}->{type} == START_TAG_TOKEN and | ||
| $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) { | ||
| # permitted slash | ||
| !!!cp (50); | ||
| # | ||
| } else { | ||
| !!!cp (51); | ||
| !!!parse-error (type => 'nestc'); | ||
| } | ||
| ## Stay in the state | ||
| # next-input-character is already done | ||
| 1283 | redo A; | redo A; |
| 1284 | } elsif ($self->{next_char} == -1) { | } elsif ($self->{next_char} == -1) { |
| 1285 | !!!parse-error (type => 'unclosed tag'); | !!!parse-error (type => 'unclosed tag'); |
| 1286 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1287 | !!!cp (52); | !!!cp (52); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1288 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1289 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1290 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 827 sub _get_next_token ($) { | Line 1314 sub _get_next_token ($) { |
| 1314 | } else { | } else { |
| 1315 | !!!cp (56); | !!!cp (56); |
| 1316 | } | } |
| 1317 | $self->{current_attribute} = {name => chr ($self->{next_char}), | $self->{current_attribute} |
| 1318 | value => ''}; | = {name => chr ($self->{next_char}), |
| 1319 | value => '', | |
| 1320 | line => $self->{line}, column => $self->{column}}; | |
| 1321 | $self->{state} = ATTRIBUTE_NAME_STATE; | $self->{state} = ATTRIBUTE_NAME_STATE; |
| 1322 | !!!next-input-character; | !!!next-input-character; |
| 1323 | redo A; | redo A; |
| # | Line 838 sub _get_next_token ($) { | Line 1327 sub _get_next_token ($) { |
| 1327 | if (exists $self->{current_token}->{attributes} # start tag or end tag | if (exists $self->{current_token}->{attributes} # start tag or end tag |
| 1328 | ->{$self->{current_attribute}->{name}}) { # MUST | ->{$self->{current_attribute}->{name}}) { # MUST |
| 1329 | !!!cp (57); | !!!cp (57); |
| 1330 | !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}); | !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column}); |
| 1331 | ## Discard $self->{current_attribute} # MUST | ## Discard $self->{current_attribute} # MUST |
| 1332 | } else { | } else { |
| 1333 | !!!cp (58); | !!!cp (58); |
| # | Line 867 sub _get_next_token ($) { | Line 1356 sub _get_next_token ($) { |
| 1356 | $before_leave->(); | $before_leave->(); |
| 1357 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1358 | !!!cp (61); | !!!cp (61); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1359 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1360 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1361 | !!!cp (62); | !!!cp (62); |
| # | Line 893 sub _get_next_token ($) { | Line 1380 sub _get_next_token ($) { |
| 1380 | !!!next-input-character; | !!!next-input-character; |
| 1381 | redo A; | redo A; |
| 1382 | } elsif ($self->{next_char} == 0x002F) { # / | } elsif ($self->{next_char} == 0x002F) { # / |
| 1383 | !!!cp (64); | |
| 1384 | $before_leave->(); | $before_leave->(); |
| 1385 | $self->{state} = SELF_CLOSING_START_TAG_STATE; | |
| 1386 | !!!next-input-character; | !!!next-input-character; |
| if ($self->{next_char} == 0x003E and # > | ||
| $self->{current_token}->{type} == START_TAG_TOKEN and | ||
| $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) { | ||
| # permitted slash | ||
| !!!cp (64); | ||
| # | ||
| } else { | ||
| !!!cp (65); | ||
| !!!parse-error (type => 'nestc'); | ||
| } | ||
| $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE; | ||
| # next-input-character is already done | ||
| 1387 | redo A; | redo A; |
| 1388 | } elsif ($self->{next_char} == -1) { | } elsif ($self->{next_char} == -1) { |
| 1389 | !!!parse-error (type => 'unclosed tag'); | !!!parse-error (type => 'unclosed tag'); |
| 1390 | $before_leave->(); | $before_leave->(); |
| 1391 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1392 | !!!cp (66); | !!!cp (66); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1393 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1394 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1395 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 965 sub _get_next_token ($) { | Line 1440 sub _get_next_token ($) { |
| 1440 | } elsif ($self->{next_char} == 0x003E) { # > | } elsif ($self->{next_char} == 0x003E) { # > |
| 1441 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1442 | !!!cp (73); | !!!cp (73); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1443 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1444 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1445 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 989 sub _get_next_token ($) { | Line 1462 sub _get_next_token ($) { |
| 1462 | } elsif (0x0041 <= $self->{next_char} and | } elsif (0x0041 <= $self->{next_char} and |
| 1463 | $self->{next_char} <= 0x005A) { # A..Z | $self->{next_char} <= 0x005A) { # A..Z |
| 1464 | !!!cp (76); | !!!cp (76); |
| 1465 | $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020), | $self->{current_attribute} |
| 1466 | value => ''}; | = {name => chr ($self->{next_char} + 0x0020), |
| 1467 | value => '', | |
| 1468 | line => $self->{line}, column => $self->{column}}; | |
| 1469 | $self->{state} = ATTRIBUTE_NAME_STATE; | $self->{state} = ATTRIBUTE_NAME_STATE; |
| 1470 | !!!next-input-character; | !!!next-input-character; |
| 1471 | redo A; | redo A; |
| 1472 | } elsif ($self->{next_char} == 0x002F) { # / | } elsif ($self->{next_char} == 0x002F) { # / |
| 1473 | !!!cp (77); | |
| 1474 | $self->{state} = SELF_CLOSING_START_TAG_STATE; | |
| 1475 | !!!next-input-character; | !!!next-input-character; |
| if ($self->{next_char} == 0x003E and # > | ||
| $self->{current_token}->{type} == START_TAG_TOKEN and | ||
| $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) { | ||
| # permitted slash | ||
| !!!cp (77); | ||
| # | ||
| } else { | ||
| !!!cp (78); | ||
| !!!parse-error (type => 'nestc'); | ||
| ## TODO: Different error type for <aa / bb> than <aa/> | ||
| } | ||
| $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE; | ||
| # next-input-character is already done | ||
| 1476 | redo A; | redo A; |
| 1477 | } elsif ($self->{next_char} == -1) { | } elsif ($self->{next_char} == -1) { |
| 1478 | !!!parse-error (type => 'unclosed tag'); | !!!parse-error (type => 'unclosed tag'); |
| 1479 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1480 | !!!cp (79); | !!!cp (79); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1481 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1482 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1483 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 1037 sub _get_next_token ($) { | Line 1499 sub _get_next_token ($) { |
| 1499 | redo A; | redo A; |
| 1500 | } else { | } else { |
| 1501 | !!!cp (82); | !!!cp (82); |
| 1502 | $self->{current_attribute} = {name => chr ($self->{next_char}), | $self->{current_attribute} |
| 1503 | value => ''}; | = {name => chr ($self->{next_char}), |
| 1504 | value => '', | |
| 1505 | line => $self->{line}, column => $self->{column}}; | |
| 1506 | $self->{state} = ATTRIBUTE_NAME_STATE; | $self->{state} = ATTRIBUTE_NAME_STATE; |
| 1507 | !!!next-input-character; | !!!next-input-character; |
| 1508 | redo A; | redo A; |
| # | Line 1071 sub _get_next_token ($) { | Line 1535 sub _get_next_token ($) { |
| 1535 | } elsif ($self->{next_char} == 0x003E) { # > | } elsif ($self->{next_char} == 0x003E) { # > |
| 1536 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1537 | !!!cp (87); | !!!cp (87); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1538 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1539 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1540 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 1096 sub _get_next_token ($) { | Line 1558 sub _get_next_token ($) { |
| 1558 | !!!parse-error (type => 'unclosed tag'); | !!!parse-error (type => 'unclosed tag'); |
| 1559 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1560 | !!!cp (90); | !!!cp (90); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1561 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1562 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1563 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 1145 sub _get_next_token ($) { | Line 1605 sub _get_next_token ($) { |
| 1605 | !!!parse-error (type => 'unclosed attribute value'); | !!!parse-error (type => 'unclosed attribute value'); |
| 1606 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1607 | !!!cp (97); | !!!cp (97); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1608 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1609 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1610 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 1189 sub _get_next_token ($) { | Line 1647 sub _get_next_token ($) { |
| 1647 | !!!parse-error (type => 'unclosed attribute value'); | !!!parse-error (type => 'unclosed attribute value'); |
| 1648 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1649 | !!!cp (103); | !!!cp (103); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1650 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1651 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1652 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 1236 sub _get_next_token ($) { | Line 1692 sub _get_next_token ($) { |
| 1692 | } elsif ($self->{next_char} == 0x003E) { # > | } elsif ($self->{next_char} == 0x003E) { # > |
| 1693 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1694 | !!!cp (109); | !!!cp (109); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1695 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1696 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1697 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 1261 sub _get_next_token ($) { | Line 1715 sub _get_next_token ($) { |
| 1715 | !!!parse-error (type => 'unclosed tag'); | !!!parse-error (type => 'unclosed tag'); |
| 1716 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1717 | !!!cp (112); | !!!cp (112); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1718 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1719 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1720 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 1333 sub _get_next_token ($) { | Line 1785 sub _get_next_token ($) { |
| 1785 | } elsif ($self->{next_char} == 0x003E) { # > | } elsif ($self->{next_char} == 0x003E) { # > |
| 1786 | if ($self->{current_token}->{type} == START_TAG_TOKEN) { | if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1787 | !!!cp (119); | !!!cp (119); |
| $self->{current_token}->{first_start_tag} | ||
| = not defined $self->{last_emitted_start_tag_name}; | ||
| 1788 | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; | $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 1789 | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { | } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) { |
| 1790 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST |
| # | Line 1355 sub _get_next_token ($) { | Line 1805 sub _get_next_token ($) { |
| 1805 | ||
| 1806 | redo A; | redo A; |
| 1807 | } elsif ($self->{next_char} == 0x002F) { # / | } elsif ($self->{next_char} == 0x002F) { # / |
| 1808 | !!!cp (122); | |
| 1809 | $self->{state} = SELF_CLOSING_START_TAG_STATE; | |
| 1810 | !!!next-input-character; | !!!next-input-character; |
| if ($self->{next_char} == 0x003E and # > | ||
| $self->{current_token}->{type} == START_TAG_TOKEN and | ||
| $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) { | ||
| # permitted slash | ||
| !!!cp (122); | ||
| # | ||
| } else { | ||
| !!!cp (123); | ||
| !!!parse-error (type => 'nestc'); | ||
| } | ||
| $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE; | ||
| # next-input-character is already done | ||
| 1811 | redo A; | redo A; |
| 1812 | } else { | } else { |
| 1813 | !!!cp (124); | !!!cp ('124.1'); |
| 1814 | !!!parse-error (type => 'no space between attributes'); | !!!parse-error (type => 'no space between attributes'); |
| 1815 | $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE; | $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE; |
| 1816 | ## reconsume | ## reconsume |
| 1817 | redo A; | redo A; |
| 1818 | } | } |
| 1819 | } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) { | |
| 1820 | if ($self->{next_char} == 0x003E) { # > | |
| 1821 | if ($self->{current_token}->{type} == END_TAG_TOKEN) { | |
| 1822 | !!!cp ('124.2'); | |
| 1823 | !!!parse-error (type => 'nestc', token => $self->{current_token}); | |
| 1824 | ## TODO: Different type than slash in start tag | |
| 1825 | $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST | |
| 1826 | if ($self->{current_token}->{attributes}) { | |
| 1827 | !!!cp ('124.4'); | |
| 1828 | !!!parse-error (type => 'end tag attribute'); | |
| 1829 | } else { | |
| 1830 | !!!cp ('124.5'); | |
| 1831 | } | |
| 1832 | ## TODO: Test |<title></title/>| | |
| 1833 | } else { | |
| 1834 | !!!cp ('124.3'); | |
| 1835 | $self->{self_closing} = 1; | |
| 1836 | } | |
| 1837 | ||
| 1838 | $self->{state} = DATA_STATE; | |
| 1839 | !!!next-input-character; | |
| 1840 | ||
| 1841 | !!!emit ($self->{current_token}); # start tag or end tag | |
| 1842 | ||
| 1843 | redo A; | |
| 1844 | } else { | |
| 1845 | !!!cp ('124.4'); | |
| 1846 | !!!parse-error (type => 'nestc'); | |
| 1847 | ## TODO: This error type is wrong. | |
| 1848 | $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE; | |
| 1849 | ## Reconsume. | |
| 1850 | redo A; | |
| 1851 | } | |
| 1852 | } elsif ($self->{state} == BOGUS_COMMENT_STATE) { | } elsif ($self->{state} == BOGUS_COMMENT_STATE) { |
| 1853 | ## (only happen if PCDATA state) | ## (only happen if PCDATA state) |
| 1854 | ||
| 1855 | my $token = {type => COMMENT_TOKEN, data => ''}; | ## NOTE: Set by the previous state |
| 1856 | #my $token = {type => COMMENT_TOKEN, data => ''}; | |
| 1857 | ||
| 1858 | BC: { | BC: { |
| 1859 | if ($self->{next_char} == 0x003E) { # > | if ($self->{next_char} == 0x003E) { # > |
| # | Line 1387 sub _get_next_token ($) { | Line 1861 sub _get_next_token ($) { |
| 1861 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1862 | !!!next-input-character; | !!!next-input-character; |
| 1863 | ||
| 1864 | !!!emit ($token); | !!!emit ($self->{current_token}); # comment |
| 1865 | ||
| 1866 | redo A; | redo A; |
| 1867 | } elsif ($self->{next_char} == -1) { | } elsif ($self->{next_char} == -1) { |
| # | Line 1395 sub _get_next_token ($) { | Line 1869 sub _get_next_token ($) { |
| 1869 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 1870 | ## reconsume | ## reconsume |
| 1871 | ||
| 1872 | !!!emit ($token); | !!!emit ($self->{current_token}); # comment |
| 1873 | ||
| 1874 | redo A; | redo A; |
| 1875 | } else { | } else { |
| 1876 | !!!cp (126); | !!!cp (126); |
| 1877 | $token->{data} .= chr ($self->{next_char}); | $self->{current_token}->{data} .= chr ($self->{next_char}); # comment |
| 1878 | !!!next-input-character; | !!!next-input-character; |
| 1879 | redo BC; | redo BC; |
| 1880 | } | } |
| # | Line 1410 sub _get_next_token ($) { | Line 1884 sub _get_next_token ($) { |
| 1884 | } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) { | } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) { |
| 1885 | ## (only happen if PCDATA state) | ## (only happen if PCDATA state) |
| 1886 | ||
| 1887 | my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); | |
| 1888 | ||
| 1889 | my @next_char; | my @next_char; |
| 1890 | push @next_char, $self->{next_char}; | push @next_char, $self->{next_char}; |
| 1891 | ||
| # | Line 1418 sub _get_next_token ($) { | Line 1894 sub _get_next_token ($) { |
| 1894 | push @next_char, $self->{next_char}; | push @next_char, $self->{next_char}; |
| 1895 | if ($self->{next_char} == 0x002D) { # - | if ($self->{next_char} == 0x002D) { # - |
| 1896 | !!!cp (127); | !!!cp (127); |
| 1897 | $self->{current_token} = {type => COMMENT_TOKEN, data => ''}; | $self->{current_token} = {type => COMMENT_TOKEN, data => '', |
| 1898 | line => $l, column => $c, | |
| 1899 | }; | |
| 1900 | $self->{state} = COMMENT_START_STATE; | $self->{state} = COMMENT_START_STATE; |
| 1901 | !!!next-input-character; | !!!next-input-character; |
| 1902 | redo A; | redo A; |
| # | Line 1454 sub _get_next_token ($) { | Line 1932 sub _get_next_token ($) { |
| 1932 | !!!cp (129); | !!!cp (129); |
| 1933 | ## TODO: What a stupid code this is! | ## TODO: What a stupid code this is! |
| 1934 | $self->{state} = DOCTYPE_STATE; | $self->{state} = DOCTYPE_STATE; |
| 1935 | $self->{current_token} = {type => DOCTYPE_TOKEN, | |
| 1936 | quirks => 1, | |
| 1937 | line => $l, column => $c, | |
| 1938 | }; | |
| 1939 | !!!next-input-character; | !!!next-input-character; |
| 1940 | redo A; | redo A; |
| 1941 | } else { | } else { |
| # | Line 1474 sub _get_next_token ($) { | Line 1956 sub _get_next_token ($) { |
| 1956 | } else { | } else { |
| 1957 | !!!cp (135); | !!!cp (135); |
| 1958 | } | } |
| 1959 | } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and | |
| 1960 | $self->{open_elements}->[-1]->[1] & FOREIGN_EL and | |
| 1961 | $self->{next_char} == 0x005B) { # [ | |
| 1962 | !!!next-input-character; | |
| 1963 | push @next_char, $self->{next_char}; | |
| 1964 | if ($self->{next_char} == 0x0043) { # C | |
| 1965 | !!!next-input-character; | |
| 1966 | push @next_char, $self->{next_char}; | |
| 1967 | if ($self->{next_char} == 0x0044) { # D | |
| 1968 | !!!next-input-character; | |
| 1969 | push @next_char, $self->{next_char}; | |
| 1970 | if ($self->{next_char} == 0x0041) { # A | |
| 1971 | !!!next-input-character; | |
| 1972 | push @next_char, $self->{next_char}; | |
| 1973 | if ($self->{next_char} == 0x0054) { # T | |
| 1974 | !!!next-input-character; | |
| 1975 | push @next_char, $self->{next_char}; | |
| 1976 | if ($self->{next_char} == 0x0041) { # A | |
| 1977 | !!!next-input-character; | |
| 1978 | push @next_char, $self->{next_char}; | |
| 1979 | if ($self->{next_char} == 0x005B) { # [ | |
| 1980 | !!!cp (135.1); | |
| 1981 | $self->{state} = CDATA_BLOCK_STATE; | |
| 1982 | !!!next-input-character; | |
| 1983 | redo A; | |
| 1984 | } else { | |
| 1985 | !!!cp (135.2); | |
| 1986 | } | |
| 1987 | } else { | |
| 1988 | !!!cp (135.3); | |
| 1989 | } | |
| 1990 | } else { | |
| 1991 | !!!cp (135.4); | |
| 1992 | } | |
| 1993 | } else { | |
| 1994 | !!!cp (135.5); | |
| 1995 | } | |
| 1996 | } else { | |
| 1997 | !!!cp (135.6); | |
| 1998 | } | |
| 1999 | } else { | |
| 2000 | !!!cp (135.7); | |
| 2001 | } | |
| 2002 | } else { | } else { |
| 2003 | !!!cp (136); | !!!cp (136); |
| 2004 | } | } |
| # | Line 1482 sub _get_next_token ($) { | Line 2007 sub _get_next_token ($) { |
| 2007 | $self->{next_char} = shift @next_char; | $self->{next_char} = shift @next_char; |
| 2008 | !!!back-next-input-character (@next_char); | !!!back-next-input-character (@next_char); |
| 2009 | $self->{state} = BOGUS_COMMENT_STATE; | $self->{state} = BOGUS_COMMENT_STATE; |
| 2010 | $self->{current_token} = {type => COMMENT_TOKEN, data => '', | |
| 2011 | line => $l, column => $c, | |
| 2012 | }; | |
| 2013 | redo A; | redo A; |
| 2014 | ||
| 2015 | ## ISSUE: typos in spec: chacacters, is is a parse error | ## ISSUE: typos in spec: chacacters, is is a parse error |
| # | Line 1605 sub _get_next_token ($) { | Line 2133 sub _get_next_token ($) { |
| 2133 | redo A; | redo A; |
| 2134 | } elsif ($self->{next_char} == 0x002D) { # - | } elsif ($self->{next_char} == 0x002D) { # - |
| 2135 | !!!cp (152); | !!!cp (152); |
| 2136 | !!!parse-error (type => 'dash in comment'); | !!!parse-error (type => 'dash in comment', |
| 2137 | line => $self->{line_prev}, | |
| 2138 | column => $self->{column_prev}); | |
| 2139 | $self->{current_token}->{data} .= '-'; # comment | $self->{current_token}->{data} .= '-'; # comment |
| 2140 | ## Stay in the state | ## Stay in the state |
| 2141 | !!!next-input-character; | !!!next-input-character; |
| # | Line 1621 sub _get_next_token ($) { | Line 2151 sub _get_next_token ($) { |
| 2151 | redo A; | redo A; |
| 2152 | } else { | } else { |
| 2153 | !!!cp (154); | !!!cp (154); |
| 2154 | !!!parse-error (type => 'dash in comment'); | !!!parse-error (type => 'dash in comment', |
| 2155 | line => $self->{line_prev}, | |
| 2156 | column => $self->{column_prev}); | |
| 2157 | $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment | $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment |
| 2158 | $self->{state} = COMMENT_STATE; | $self->{state} = COMMENT_STATE; |
| 2159 | !!!next-input-character; | !!!next-input-character; |
| # | Line 1660 sub _get_next_token ($) { | Line 2192 sub _get_next_token ($) { |
| 2192 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 2193 | !!!next-input-character; | !!!next-input-character; |
| 2194 | ||
| 2195 | !!!emit ({type => DOCTYPE_TOKEN, quirks => 1}); | !!!emit ($self->{current_token}); # DOCTYPE (quirks) |
| 2196 | ||
| 2197 | redo A; | redo A; |
| 2198 | } elsif ($self->{next_char} == -1) { | } elsif ($self->{next_char} == -1) { |
| # | Line 1669 sub _get_next_token ($) { | Line 2201 sub _get_next_token ($) { |
| 2201 | $self->{state} = DATA_STATE; | $self->{state} = DATA_STATE; |
| 2202 | ## reconsume | ## reconsume |
| 2203 | ||
| 2204 | !!!emit ({type => DOCTYPE_TOKEN, quirks => 1}); | !!!emit ($self->{current_token}); # DOCTYPE (quirks) |
| 2205 | ||
| 2206 | redo A; | redo A; |
| 2207 | } else { | } else { |
| 2208 | !!!cp (160); | !!!cp (160); |
| 2209 | $self->{current_token} | $self->{current_token}->{name} = chr $self->{next_char}; |
| 2210 | = {type => DOCTYPE_TOKEN, | delete $self->{current_token}->{quirks}; |
| name => chr ($self->{next_char}), | ||
| #quirks => 0, | ||
| }; | ||
| 2211 | ## ISSUE: "Set the token's name name to the" in the spec | ## ISSUE: "Set the token's name name to the" in the spec |
| 2212 | $self->{state} = DOCTYPE_NAME_STATE; | $self->{state} = DOCTYPE_NAME_STATE; |
| 2213 | !!!next-input-character; | !!!next-input-character; |
| # | Line 2194 sub _get_next_token ($) { | Line 2723 sub _get_next_token ($) { |
| 2723 | !!!next-input-character; | !!!next-input-character; |
| 2724 | redo A; | redo A; |
| 2725 | } | } |
| 2726 | } elsif ($self->{state} == CDATA_BLOCK_STATE) { | |
| 2727 | my $s = ''; | |
| 2728 | ||
| 2729 | my ($l, $c) = ($self->{line}, $self->{column}); | |
| 2730 | ||
| 2731 | CS: while ($self->{next_char} != -1) { | |
| 2732 | if ($self->{next_char} == 0x005D) { # ] | |
| 2733 | !!!next-input-character; | |
| 2734 | if ($self->{next_char} == 0x005D) { # ] | |
| 2735 | !!!next-input-character; | |
| 2736 | MDC: { | |
| 2737 | if ($self->{next_char} == 0x003E) { # > | |
| 2738 | !!!cp (221.1); | |
| 2739 | !!!next-input-character; | |
| 2740 | last CS; | |
| 2741 | } elsif ($self->{next_char} == 0x005D) { # ] | |
| 2742 | !!!cp (221.2); | |
| 2743 | $s .= ']'; | |
| 2744 | !!!next-input-character; | |
| 2745 | redo MDC; | |
| 2746 | } else { | |
| 2747 | !!!cp (221.3); | |
| 2748 | $s .= ']]'; | |
| 2749 | # | |
| 2750 | } | |
| 2751 | } # MDC | |
| 2752 | } else { | |
| 2753 | !!!cp (221.4); | |
| 2754 | $s .= ']'; | |
| 2755 | # | |
| 2756 | } | |
| 2757 | } else { | |
| 2758 | !!!cp (221.5); | |
| 2759 | # | |
| 2760 | } | |
| 2761 | $s .= chr $self->{next_char}; | |
| 2762 | !!!next-input-character; | |
| 2763 | } # CS | |
| 2764 | ||
| 2765 | $self->{state} = DATA_STATE; | |
| 2766 | ## next-input-character done or EOF, which is reconsumed. | |
| 2767 | ||
| 2768 | if (length $s) { | |
| 2769 | !!!cp (221.6); | |
| 2770 | !!!emit ({type => CHARACTER_TOKEN, data => $s, | |
| 2771 | line => $l, column => $c}); | |
| 2772 | } else { | |
| 2773 | !!!cp (221.7); | |
| 2774 | } | |
| 2775 | ||
| 2776 | redo A; | |
| 2777 | ||
| 2778 | ## ISSUE: "text tokens" in spec. | |
| 2779 | ## TODO: Streaming support | |
| 2780 | } else { | } else { |
| 2781 | die "$0: $self->{state}: Unknown state"; | die "$0: $self->{state}: Unknown state"; |
| 2782 | } | } |
| # | Line 2205 sub _get_next_token ($) { | Line 2788 sub _get_next_token ($) { |
| 2788 | sub _tokenize_attempt_to_consume_an_entity ($$$) { | sub _tokenize_attempt_to_consume_an_entity ($$$) { |
| 2789 | my ($self, $in_attr, $additional) = @_; | my ($self, $in_attr, $additional) = @_; |
| 2790 | ||
| 2791 | my ($l, $c) = ($self->{line_prev}, $self->{column_prev}); | |
| 2792 | ||
| 2793 | if ({ | if ({ |
| 2794 | 0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF, | 0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF, |
| 2795 | 0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR | 0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR |
| # | Line 2245 sub _tokenize_attempt_to_consume_an_enti | Line 2830 sub _tokenize_attempt_to_consume_an_enti |
| 2830 | redo X; | redo X; |
| 2831 | } elsif (not defined $code) { # no hexadecimal digit | } elsif (not defined $code) { # no hexadecimal digit |
| 2832 | !!!cp (1005); | !!!cp (1005); |
| 2833 | !!!parse-error (type => 'bare hcro'); | !!!parse-error (type => 'bare hcro', line => $l, column => $c); |
| 2834 | !!!back-next-input-character ($x_char, $self->{next_char}); | !!!back-next-input-character ($x_char, $self->{next_char}); |
| 2835 | $self->{next_char} = 0x0023; # # | $self->{next_char} = 0x0023; # # |
| 2836 | return undef; | return undef; |
| # | Line 2254 sub _tokenize_attempt_to_consume_an_enti | Line 2839 sub _tokenize_attempt_to_consume_an_enti |
| 2839 | !!!next-input-character; | !!!next-input-character; |
| 2840 | } else { | } else { |
| 2841 | !!!cp (1007); | !!!cp (1007); |
| 2842 | !!!parse-error (type => 'no refc'); | !!!parse-error (type => 'no refc', line => $l, column => $c); |
| 2843 | } | } |
| 2844 | ||
| 2845 | if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) { | if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) { |
| 2846 | !!!cp (1008); | !!!cp (1008); |
| 2847 | !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code); | !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c); |
| 2848 | $code = 0xFFFD; | $code = 0xFFFD; |
| 2849 | } elsif ($code > 0x10FFFF) { | } elsif ($code > 0x10FFFF) { |
| 2850 | !!!cp (1009); | !!!cp (1009); |
| 2851 | !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code); | !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c); |
| 2852 | $code = 0xFFFD; | $code = 0xFFFD; |
| 2853 | } elsif ($code == 0x000D) { | } elsif ($code == 0x000D) { |
| 2854 | !!!cp (1010); | !!!cp (1010); |
| 2855 | !!!parse-error (type => 'CR character reference'); | !!!parse-error (type => 'CR character reference', line => $l, column => $c); |
| 2856 | $code = 0x000A; | $code = 0x000A; |
| 2857 | } elsif (0x80 <= $code and $code <= 0x9F) { | } elsif (0x80 <= $code and $code <= 0x9F) { |
| 2858 | !!!cp (1011); | !!!cp (1011); |
| 2859 | !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code); | !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c); |
| 2860 | $code = $c1_entity_char->{$code}; | $code = $c1_entity_char->{$code}; |
| 2861 | } | } |
| 2862 | ||
| 2863 | return {type => CHARACTER_TOKEN, data => chr $code, | return {type => CHARACTER_TOKEN, data => chr $code, |
| 2864 | has_reference => 1}; | has_reference => 1, |
| 2865 | line => $l, column => $c, | |
| 2866 | }; | |
| 2867 | } # X | } # X |
| 2868 | } elsif (0x0030 <= $self->{next_char} and | } elsif (0x0030 <= $self->{next_char} and |
| 2869 | $self->{next_char} <= 0x0039) { # 0..9 | $self->{next_char} <= 0x0039) { # 0..9 |
| # | Line 2297 sub _tokenize_attempt_to_consume_an_enti | Line 2884 sub _tokenize_attempt_to_consume_an_enti |
| 2884 | !!!next-input-character; | !!!next-input-character; |
| 2885 | } else { | } else { |
| 2886 | !!!cp (1014); | !!!cp (1014); |
| 2887 | !!!parse-error (type => 'no refc'); | !!!parse-error (type => 'no refc', line => $l, column => $c); |
| 2888 | } | } |
| 2889 | ||
| 2890 | if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) { | if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) { |
| 2891 | !!!cp (1015); | !!!cp (1015); |
| 2892 | !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code); | !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c); |
| 2893 | $code = 0xFFFD; | $code = 0xFFFD; |
| 2894 | } elsif ($code > 0x10FFFF) { | } elsif ($code > 0x10FFFF) { |
| 2895 | !!!cp (1016); | !!!cp (1016); |
| 2896 | !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code); | !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c); |
| 2897 | $code = 0xFFFD; | $code = 0xFFFD; |
| 2898 | } elsif ($code == 0x000D) { | } elsif ($code == 0x000D) { |
| 2899 | !!!cp (1017); | !!!cp (1017); |
| 2900 | !!!parse-error (type => 'CR character reference'); | !!!parse-error (type => 'CR character reference', line => $l, column => $c); |
| 2901 | $code = 0x000A; | $code = 0x000A; |
| 2902 | } elsif (0x80 <= $code and $code <= 0x9F) { | } elsif (0x80 <= $code and $code <= 0x9F) { |
| 2903 | !!!cp (1018); | !!!cp (1018); |
| 2904 | !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code); | !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c); |
| 2905 | $code = $c1_entity_char->{$code}; | $code = $c1_entity_char->{$code}; |
| 2906 | } | } |
| 2907 | ||
| 2908 | return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1}; | return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1, |
| 2909 | line => $l, column => $c, | |
| 2910 | }; | |
| 2911 | } else { | } else { |
| 2912 | !!!cp (1019); | !!!cp (1019); |
| 2913 | !!!parse-error (type => 'bare nero'); | !!!parse-error (type => 'bare nero', line => $l, column => $c); |
| 2914 | !!!back-next-input-character ($self->{next_char}); | !!!back-next-input-character ($self->{next_char}); |
| 2915 | $self->{next_char} = 0x0023; # # | $self->{next_char} = 0x0023; # # |
| 2916 | return undef; | return undef; |
| # | Line 2338 sub _tokenize_attempt_to_consume_an_enti | Line 2927 sub _tokenize_attempt_to_consume_an_enti |
| 2927 | require Whatpm::_NamedEntityList; | require Whatpm::_NamedEntityList; |
| 2928 | our $EntityChar; | our $EntityChar; |
| 2929 | ||
| 2930 | while (length $entity_name < 10 and | while (length $entity_name < 30 and |
| 2931 | ## NOTE: Some number greater than the maximum length of entity name | ## NOTE: Some number greater than the maximum length of entity name |
| 2932 | ((0x0041 <= $self->{next_char} and # a | ((0x0041 <= $self->{next_char} and # a |
| 2933 | $self->{next_char} <= 0x005A) or # x | $self->{next_char} <= 0x005A) or # x |
| # | Line 2371 sub _tokenize_attempt_to_consume_an_enti | Line 2960 sub _tokenize_attempt_to_consume_an_enti |
| 2960 | ||
| 2961 | if ($match > 0) { | if ($match > 0) { |
| 2962 | !!!cp (1023); | !!!cp (1023); |
| 2963 | return {type => CHARACTER_TOKEN, data => $value, has_reference => 1}; | return {type => CHARACTER_TOKEN, data => $value, has_reference => 1, |
| 2964 | line => $l, column => $c, | |
| 2965 | }; | |
| 2966 | } elsif ($match < 0) { | } elsif ($match < 0) { |
| 2967 | !!!parse-error (type => 'no refc'); | !!!parse-error (type => 'no refc', line => $l, column => $c); |
| 2968 | if ($in_attr and $match < -1) { | if ($in_attr and $match < -1) { |
| 2969 | !!!cp (1024); | !!!cp (1024); |
| 2970 | return {type => CHARACTER_TOKEN, data => '&'.$entity_name}; | return {type => CHARACTER_TOKEN, data => '&'.$entity_name, |
| 2971 | line => $l, column => $c, | |
| 2972 | }; | |
| 2973 | } else { | } else { |
| 2974 | !!!cp (1025); | !!!cp (1025); |
| 2975 | return {type => CHARACTER_TOKEN, data => $value, has_reference => 1}; | return {type => CHARACTER_TOKEN, data => $value, has_reference => 1, |
| 2976 | line => $l, column => $c, | |
| 2977 | }; | |
| 2978 | } | } |
| 2979 | } else { | } else { |
| 2980 | !!!cp (1026); | !!!cp (1026); |
| 2981 | !!!parse-error (type => 'bare ero'); | !!!parse-error (type => 'bare ero', line => $l, column => $c); |
| 2982 | ## NOTE: "No characters are consumed" in the spec. | ## NOTE: "No characters are consumed" in the spec. |
| 2983 | return {type => CHARACTER_TOKEN, data => '&'.$value}; | return {type => CHARACTER_TOKEN, data => '&'.$value, |
| 2984 | line => $l, column => $c, | |
| 2985 | }; | |
| 2986 | } | } |
| 2987 | } else { | } else { |
| 2988 | !!!cp (1027); | !!!cp (1027); |
| 2989 | ## no characters are consumed | ## no characters are consumed |
| 2990 | !!!parse-error (type => 'bare ero'); | !!!parse-error (type => 'bare ero', line => $l, column => $c); |
| 2991 | return undef; | return undef; |
| 2992 | } | } |
| 2993 | } # _tokenize_attempt_to_consume_an_entity | } # _tokenize_attempt_to_consume_an_entity |
| # | Line 2461 sub _tree_construction_initial ($) { | Line 3058 sub _tree_construction_initial ($) { |
| 3058 | defined $token->{public_identifier} or | defined $token->{public_identifier} or |
| 3059 | defined $token->{system_identifier}) { | defined $token->{system_identifier}) { |
| 3060 | !!!cp ('t1'); | !!!cp ('t1'); |
| 3061 | !!!parse-error (type => 'not HTML5'); | !!!parse-error (type => 'not HTML5', token => $token); |
| 3062 | } elsif ($doctype_name ne 'HTML') { | } elsif ($doctype_name ne 'HTML') { |
| 3063 | !!!cp ('t2'); | !!!cp ('t2'); |
| 3064 | ## ISSUE: ASCII case-insensitive? (in fact it does not matter) | ## ISSUE: ASCII case-insensitive? (in fact it does not matter) |
| 3065 | !!!parse-error (type => 'not HTML5'); | !!!parse-error (type => 'not HTML5', token => $token); |
| 3066 | } else { | } else { |
| 3067 | !!!cp ('t3'); | !!!cp ('t3'); |
| 3068 | } | } |
| 3069 | ||
| 3070 | my $doctype = $self->{document}->create_document_type_definition | my $doctype = $self->{document}->create_document_type_definition |
| 3071 | ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)? | ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)? |
| 3072 | ## NOTE: Default value for both |public_id| and |system_id| attributes | |
| 3073 | ## are empty strings, so that we don't set any value in missing cases. | |
| 3074 | $doctype->public_id ($token->{public_identifier}) | $doctype->public_id ($token->{public_identifier}) |
| 3075 | if defined $token->{public_identifier}; | if defined $token->{public_identifier}; |
| 3076 | $doctype->system_id ($token->{system_identifier}) | $doctype->system_id ($token->{system_identifier}) |
| # | Line 2604 sub _tree_construction_initial ($) { | Line 3203 sub _tree_construction_initial ($) { |
| 3203 | END_OF_FILE_TOKEN, 1, | END_OF_FILE_TOKEN, 1, |
| 3204 | }->{$token->{type}}) { | }->{$token->{type}}) { |
| 3205 | !!!cp ('t14'); | !!!cp ('t14'); |
| 3206 | !!!parse-error (type => 'no DOCTYPE'); | !!!parse-error (type => 'no DOCTYPE', token => $token); |
| 3207 | $self->{document}->manakai_compat_mode ('quirks'); | $self->{document}->manakai_compat_mode ('quirks'); |
| 3208 | ## Go to the "before html" insertion mode. | ## Go to the "before html" insertion mode. |
| 3209 | ## reprocess | ## reprocess |
| 3210 | !!!ack-later; | |
| 3211 | return; | return; |
| 3212 | } elsif ($token->{type} == CHARACTER_TOKEN) { | } elsif ($token->{type} == CHARACTER_TOKEN) { |
| 3213 | if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D | if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D |
| # | Line 2625 sub _tree_construction_initial ($) { | Line 3225 sub _tree_construction_initial ($) { |
| 3225 | !!!cp ('t17'); | !!!cp ('t17'); |
| 3226 | } | } |
| 3227 | ||
| 3228 | !!!parse-error (type => 'no DOCTYPE'); | !!!parse-error (type => 'no DOCTYPE', token => $token); |
| 3229 | $self->{document}->manakai_compat_mode ('quirks'); | $self->{document}->manakai_compat_mode ('quirks'); |
| 3230 | ## Go to the "before html" insertion mode. | ## Go to the "before html" insertion mode. |
| 3231 | ## reprocess | ## reprocess |
| # | Line 2654 sub _tree_construction_root_element ($) | Line 3254 sub _tree_construction_root_element ($) |
| 3254 | B: { | B: { |
| 3255 | if ($token->{type} == DOCTYPE_TOKEN) { | if ($token->{type} == DOCTYPE_TOKEN) { |
| 3256 | !!!cp ('t19'); | !!!cp ('t19'); |
| 3257 | !!!parse-error (type => 'in html:#DOCTYPE'); | !!!parse-error (type => 'in html:#DOCTYPE', token => $token); |
| 3258 | ## Ignore the token | ## Ignore the token |
| 3259 | ## Stay in the insertion mode. | ## Stay in the insertion mode. |
| 3260 | !!!next-token; | !!!next-token; |
| # | Line 2688 sub _tree_construction_root_element ($) | Line 3288 sub _tree_construction_root_element ($) |
| 3288 | } elsif ($token->{type} == START_TAG_TOKEN) { | } elsif ($token->{type} == START_TAG_TOKEN) { |
| 3289 | if ($token->{tag_name} eq 'html') { | if ($token->{tag_name} eq 'html') { |
| 3290 | my $root_element; | my $root_element; |
| 3291 | !!!create-element ($root_element, $token->{tag_name}, $token->{attributes}); | !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token); |
| 3292 | $self->{document}->append_child ($root_element); | $self->{document}->append_child ($root_element); |
| 3293 | push @{$self->{open_elements}}, [$root_element, 'html']; | push @{$self->{open_elements}}, |
| 3294 | [$root_element, $el_category->{html}]; | |
| 3295 | ||
| 3296 | if ($token->{attributes}->{manifest}) { | if ($token->{attributes}->{manifest}) { |
| 3297 | !!!cp ('t24'); | !!!cp ('t24'); |
| 3298 | $self->{application_cache_selection} | $self->{application_cache_selection} |
| 3299 | ->($token->{attributes}->{manifest}->{value}); | ->($token->{attributes}->{manifest}->{value}); |
| 3300 | ## ISSUE: No relative reference resolution? | ## ISSUE: Spec is unclear on relative references. |
| 3301 | ## According to Hixie (#whatwg 2008-03-19), it should be | |
| 3302 | ## resolved against the base URI of the document in HTML | |
| 3303 | ## or xml:base of the element in XHTML. | |
| 3304 | } else { | } else { |
| 3305 | !!!cp ('t25'); | !!!cp ('t25'); |
| 3306 | $self->{application_cache_selection}->(undef); | $self->{application_cache_selection}->(undef); |
| 3307 | } | } |
| 3308 | ||
| 3309 | !!!nack ('t25c'); | |
| 3310 | ||
| 3311 | !!!next-token; | !!!next-token; |
| 3312 | return; ## Go to the "before head" insertion mode. | return; ## Go to the "before head" insertion mode. |
| 3313 | } else { | } else { |
| # | Line 2718 sub _tree_construction_root_element ($) | Line 3324 sub _tree_construction_root_element ($) |
| 3324 | die "$0: $token->{type}: Unknown token type"; | die "$0: $token->{type}: Unknown token type"; |
| 3325 | } | } |
| 3326 | ||
| 3327 | my $root_element; !!!create-element ($root_element, 'html'); | my $root_element; |
| 3328 | !!!create-element ($root_element, $HTML_NS, 'html',, $token); | |
| 3329 | $self->{document}->append_child ($root_element); | $self->{document}->append_child ($root_element); |
| 3330 | push @{$self->{open_elements}}, [$root_element, 'html']; | push @{$self->{open_elements}}, [$root_element, $el_category->{html}]; |
| 3331 | ||
| 3332 | $self->{application_cache_selection}->(undef); | $self->{application_cache_selection}->(undef); |
| 3333 | ||
| 3334 | ## NOTE: Reprocess the token. | ## NOTE: Reprocess the token. |
| 3335 | !!!ack-later; | |
| 3336 | return; ## Go to the "before head" insertion mode. | return; ## Go to the "before head" insertion mode. |
| 3337 | ||
| 3338 | ## ISSUE: There is an issue in the spec | ## ISSUE: There is an issue in the spec |
| # | Line 2748 sub _reset_insertion_mode ($) { | Line 3356 sub _reset_insertion_mode ($) { |
| 3356 | if ($self->{open_elements}->[0]->[0] eq $node->[0]) { | if ($self->{open_elements}->[0]->[0] eq $node->[0]) { |
| 3357 | $last = 1; | $last = 1; |
| 3358 | if (defined $self->{inner_html_node}) { | if (defined $self->{inner_html_node}) { |
| 3359 | if ($self->{inner_html_node}->[1] eq 'td' or | if ($self->{inner_html_node}->[1] & TABLE_CELL_EL) { |
| $self->{inner_html_node}->[1] eq 'th') { | ||
| 3360 | !!!cp ('t27'); | !!!cp ('t27'); |
| 3361 | # | # |
| 3362 | } else { | } else { |
| # | Line 2759 sub _reset_insertion_mode ($) { | Line 3366 sub _reset_insertion_mode ($) { |
| 3366 | } | } |
| 3367 | } | } |
| 3368 | ||
| 3369 | ## Step 4..13 | ## Step 4..14 |
| 3370 | my $new_mode = { | my $new_mode; |
| 3371 | if ($node->[1] & FOREIGN_EL) { | |
| 3372 | ## NOTE: Strictly spaking, the line below only applies to MathML and | |
| 3373 | ## SVG elements. Currently the HTML syntax supports only MathML and | |
| 3374 | ## SVG elements as foreigners. | |
| 3375 | $new_mode = $self->{insertion_mode} | IN_FOREIGN_CONTENT_IM; | |
| 3376 | ## ISSUE: What is set as the secondary insertion mode? | |
| 3377 | } else { | |
| 3378 | $new_mode = { | |
| 3379 | select => IN_SELECT_IM, | select => IN_SELECT_IM, |
| 3380 | ## NOTE: |option| and |optgroup| do not set | ## NOTE: |option| and |optgroup| do not set |
| 3381 | ## insertion mode to "in select" by themselves. | ## insertion mode to "in select" by themselves. |
| # | Line 2776 sub _reset_insertion_mode ($) { | Line 3391 sub _reset_insertion_mode ($) { |
| 3391 | head => IN_BODY_IM, # not in head! | head => IN_BODY_IM, # not in head! |
| 3392 | body => IN_BODY_IM, | body => IN_BODY_IM, |
| 3393 | frameset => IN_FRAMESET_IM, | frameset => IN_FRAMESET_IM, |
| 3394 | }->{$node->[1]}; | }->{$node->[0]->manakai_local_name}; |
| 3395 | $self->{insertion_mode} = $new_mode and return if defined $new_mode; | } |
| 3396 | $self->{insertion_mode} = $new_mode and return if defined $new_mode; | |
| 3397 | ||
| 3398 | ## Step 14 | ## Step 15 |
| 3399 | if ($node->[1] eq 'html') { | if ($node->[1] & HTML_EL) { |
| 3400 | unless (defined $self->{head_element}) { | unless (defined $self->{head_element}) { |
| 3401 | !!!cp ('t29'); | !!!cp ('t29'); |
| 3402 | $self->{insertion_mode} = BEFORE_HEAD_IM; | $self->{insertion_mode} = BEFORE_HEAD_IM; |
| # | Line 2794 sub _reset_insertion_mode ($) { | Line 3410 sub _reset_insertion_mode ($) { |
| 3410 | !!!cp ('t31'); | !!!cp ('t31'); |
| 3411 | } | } |
| 3412 | ||
| 3413 | ## Step 15 | ## Step 16 |
| 3414 | $self->{insertion_mode} = IN_BODY_IM and return if $last; | $self->{insertion_mode} = IN_BODY_IM and return if $last; |
| 3415 | ||
| 3416 | ## Step 16 | ## Step 17 |
| 3417 | $i--; | $i--; |
| 3418 | $node = $self->{open_elements}->[$i]; | $node = $self->{open_elements}->[$i]; |
| 3419 | ||
| 3420 | ## Step 17 | ## Step 18 |
| 3421 | redo S3; | redo S3; |
| 3422 | } # S3 | } # S3 |
| 3423 | ||
| # | Line 2913 sub _tree_construction_main ($) { | Line 3529 sub _tree_construction_main ($) { |
| 3529 | ## Step 1 | ## Step 1 |
| 3530 | my $start_tag_name = $token->{tag_name}; | my $start_tag_name = $token->{tag_name}; |
| 3531 | my $el; | my $el; |
| 3532 | !!!create-element ($el, $start_tag_name, $token->{attributes}); | !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token); |
| 3533 | ||
| 3534 | ## Step 2 | ## Step 2 |
| 3535 | $insert->($el); | $insert->($el); |
| # | Line 2924 sub _tree_construction_main ($) { | Line 3540 sub _tree_construction_main ($) { |
| 3540 | ||
| 3541 | ## Step 4 | ## Step 4 |
| 3542 | my $text = ''; | my $text = ''; |
| 3543 | !!!nack ('t40.1'); | |
| 3544 | !!!next-token; | !!!next-token; |
| 3545 | while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing | while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing |
| 3546 | !!!cp ('t40'); | !!!cp ('t40'); |
| # | Line 2950 sub _tree_construction_main ($) { | Line 3567 sub _tree_construction_main ($) { |
| 3567 | ## NOTE: An end-of-file token. | ## NOTE: An end-of-file token. |
| 3568 | if ($content_model_flag == CDATA_CONTENT_MODEL) { | if ($content_model_flag == CDATA_CONTENT_MODEL) { |
| 3569 | !!!cp ('t43'); | !!!cp ('t43'); |
| 3570 | !!!parse-error (type => 'in CDATA:#'.$token->{type}); | !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token); |
| 3571 | } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) { | } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) { |
| 3572 | !!!cp ('t44'); | !!!cp ('t44'); |
| 3573 | !!!parse-error (type => 'in RCDATA:#'.$token->{type}); | !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token); |
| 3574 | } else { | } else { |
| 3575 | die "$0: $content_model_flag in parse_rcdata"; | die "$0: $content_model_flag in parse_rcdata"; |
| 3576 | } | } |
| # | Line 2963 sub _tree_construction_main ($) { | Line 3580 sub _tree_construction_main ($) { |
| 3580 | ||
| 3581 | my $script_start_tag = sub () { | my $script_start_tag = sub () { |
| 3582 | my $script_el; | my $script_el; |
| 3583 | !!!create-element ($script_el, 'script', $token->{attributes}); | !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token); |
| 3584 | ## TODO: mark as "parser-inserted" | ## TODO: mark as "parser-inserted" |
| 3585 | ||
| 3586 | $self->{content_model} = CDATA_CONTENT_MODEL; | $self->{content_model} = CDATA_CONTENT_MODEL; |
| 3587 | delete $self->{escape}; # MUST | delete $self->{escape}; # MUST |
| 3588 | ||
| 3589 | my $text = ''; | my $text = ''; |
| 3590 | !!!nack ('t45.1'); | |
| 3591 | !!!next-token; | !!!next-token; |
| 3592 | while ($token->{type} == CHARACTER_TOKEN) { | while ($token->{type} == CHARACTER_TOKEN) { |
| 3593 | !!!cp ('t45'); | !!!cp ('t45'); |
| # | Line 2989 sub _tree_construction_main ($) { | Line 3607 sub _tree_construction_main ($) { |
| 3607 | ## Ignore the token | ## Ignore the token |
| 3608 | } else { | } else { |
| 3609 | !!!cp ('t48'); | !!!cp ('t48'); |
| 3610 | !!!parse-error (type => 'in CDATA:#'.$token->{type}); | !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token); |
| 3611 | ## ISSUE: And ignore? | ## ISSUE: And ignore? |
| 3612 | ## TODO: mark as "already executed" | ## TODO: mark as "already executed" |
| 3613 | } | } |
| # | Line 3017 sub _tree_construction_main ($) { | Line 3635 sub _tree_construction_main ($) { |
| 3635 | my $open_tables = [[$self->{open_elements}->[0]->[0]]]; | my $open_tables = [[$self->{open_elements}->[0]->[0]]]; |
| 3636 | ||
| 3637 | my $formatting_end_tag = sub { | my $formatting_end_tag = sub { |
| 3638 | my $tag_name = shift; | my $end_tag_token = shift; |
| 3639 | my $tag_name = $end_tag_token->{tag_name}; | |
| 3640 | ||
| 3641 | ## NOTE: The adoption agency algorithm (AAA). | ## NOTE: The adoption agency algorithm (AAA). |
| 3642 | ||
| # | Line 3026 sub _tree_construction_main ($) { | Line 3645 sub _tree_construction_main ($) { |
| 3645 | my $formatting_element; | my $formatting_element; |
| 3646 | my $formatting_element_i_in_active; | my $formatting_element_i_in_active; |
| 3647 | AFE: for (reverse 0..$#$active_formatting_elements) { | AFE: for (reverse 0..$#$active_formatting_elements) { |
| 3648 | if ($active_formatting_elements->[$_]->[1] eq $tag_name) { | if ($active_formatting_elements->[$_]->[0] eq '#marker') { |
| 3649 | !!!cp ('t52'); | |
| 3650 | last AFE; | |
| 3651 | } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name | |
| 3652 | eq $tag_name) { | |
| 3653 | !!!cp ('t51'); | !!!cp ('t51'); |
| 3654 | $formatting_element = $active_formatting_elements->[$_]; | $formatting_element = $active_formatting_elements->[$_]; |
| 3655 | $formatting_element_i_in_active = $_; | $formatting_element_i_in_active = $_; |
| 3656 | last AFE; | last AFE; |
| } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') { | ||
| !!!cp ('t52'); | ||
| last AFE; | ||
| 3657 | } | } |
| 3658 | } # AFE | } # AFE |
| 3659 | unless (defined $formatting_element) { | unless (defined $formatting_element) { |
| 3660 | !!!cp ('t53'); | !!!cp ('t53'); |
| 3661 | !!!parse-error (type => 'unmatched end tag:'.$tag_name); | !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token); |
| 3662 | ## Ignore the token | ## Ignore the token |
| 3663 | !!!next-token; | !!!next-token; |
| 3664 | return; | return; |
| # | Line 3055 sub _tree_construction_main ($) { | Line 3675 sub _tree_construction_main ($) { |
| 3675 | last INSCOPE; | last INSCOPE; |
| 3676 | } else { # in open elements but not in scope | } else { # in open elements but not in scope |
| 3677 | !!!cp ('t55'); | !!!cp ('t55'); |
| 3678 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, |
| 3679 | token => $end_tag_token); | |
| 3680 | ## Ignore the token | ## Ignore the token |
| 3681 | !!!next-token; | !!!next-token; |
| 3682 | return; | return; |
| 3683 | } | } |
| 3684 | } elsif ({ | } elsif ($node->[1] & SCOPING_EL) { |
| applet => 1, table => 1, caption => 1, td => 1, th => 1, | ||
| button => 1, marquee => 1, object => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 3685 | !!!cp ('t56'); | !!!cp ('t56'); |
| 3686 | $in_scope = 0; | $in_scope = 0; |
| 3687 | } | } |
| 3688 | } # INSCOPE | } # INSCOPE |
| 3689 | unless (defined $formatting_element_i_in_open) { | unless (defined $formatting_element_i_in_open) { |
| 3690 | !!!cp ('t57'); | !!!cp ('t57'); |
| 3691 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, |
| 3692 | token => $end_tag_token); | |
| 3693 | pop @$active_formatting_elements; # $formatting_element | pop @$active_formatting_elements; # $formatting_element |
| 3694 | !!!next-token; ## TODO: ok? | !!!next-token; ## TODO: ok? |
| 3695 | return; | return; |
| 3696 | } | } |
| 3697 | if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) { | if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) { |
| 3698 | !!!cp ('t58'); | !!!cp ('t58'); |
| 3699 | !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]); | !!!parse-error (type => 'not closed', |
| 3700 | value => $self->{open_elements}->[-1]->[0] | |
| 3701 | ->manakai_local_name, | |
| 3702 | token => $end_tag_token); | |
| 3703 | } | } |
| 3704 | ||
| 3705 | ## Step 2 | ## Step 2 |
| # | Line 3085 sub _tree_construction_main ($) { | Line 3707 sub _tree_construction_main ($) { |
| 3707 | my $furthest_block_i_in_open; | my $furthest_block_i_in_open; |
| 3708 | OE: for (reverse 0..$#{$self->{open_elements}}) { | OE: for (reverse 0..$#{$self->{open_elements}}) { |
| 3709 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 3710 | if (not $formatting_category->{$node->[1]} and | if (not ($node->[1] & FORMATTING_EL) and |
| 3711 | #not $phrasing_category->{$node->[1]} and | #not $phrasing_category->{$node->[1]} and |
| 3712 | ($special_category->{$node->[1]} or | ($node->[1] & SPECIAL_EL or |
| 3713 | $scoping_category->{$node->[1]})) { ## Scoping is redundant, maybe | $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe |
| 3714 | !!!cp ('t59'); | !!!cp ('t59'); |
| 3715 | $furthest_block = $node; | $furthest_block = $node; |
| 3716 | $furthest_block_i_in_open = $_; | $furthest_block_i_in_open = $_; |
| # | Line 3174 sub _tree_construction_main ($) { | Line 3796 sub _tree_construction_main ($) { |
| 3796 | } # S7 | } # S7 |
| 3797 | ||
| 3798 | ## Step 8 | ## Step 8 |
| 3799 | if ({ | if ($common_ancestor_node->[1] & TABLE_ROWS_EL) { |
| table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1, | ||
| }->{$common_ancestor_node->[1]}) { | ||
| 3800 | my $foster_parent_element; | my $foster_parent_element; |
| 3801 | my $next_sibling; | my $next_sibling; |
| 3802 | OE: for (reverse 0..$#{$self->{open_elements}}) { | OE: for (reverse 0..$#{$self->{open_elements}}) { |
| 3803 | if ($self->{open_elements}->[$_]->[1] eq 'table') { | if ($self->{open_elements}->[$_]->[1] & TABLE_EL) { |
| 3804 | my $parent = $self->{open_elements}->[$_]->[0]->parent_node; | my $parent = $self->{open_elements}->[$_]->[0]->parent_node; |
| 3805 | if (defined $parent and $parent->node_type == 1) { | if (defined $parent and $parent->node_type == 1) { |
| 3806 | !!!cp ('t65.1'); | !!!cp ('t65.1'); |
| # | Line 3253 sub _tree_construction_main ($) { | Line 3873 sub _tree_construction_main ($) { |
| 3873 | ||
| 3874 | my $insert_to_foster = sub { | my $insert_to_foster = sub { |
| 3875 | my $child = shift; | my $child = shift; |
| 3876 | if ({ | if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) { |
| table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1, | ||
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 3877 | # MUST | # MUST |
| 3878 | my $foster_parent_element; | my $foster_parent_element; |
| 3879 | my $next_sibling; | my $next_sibling; |
| 3880 | OE: for (reverse 0..$#{$self->{open_elements}}) { | OE: for (reverse 0..$#{$self->{open_elements}}) { |
| 3881 | if ($self->{open_elements}->[$_]->[1] eq 'table') { | if ($self->{open_elements}->[$_]->[1] & TABLE_EL) { |
| 3882 | my $parent = $self->{open_elements}->[$_]->[0]->parent_node; | my $parent = $self->{open_elements}->[$_]->[0]->parent_node; |
| 3883 | if (defined $parent and $parent->node_type == 1) { | if (defined $parent and $parent->node_type == 1) { |
| 3884 | !!!cp ('t70'); | !!!cp ('t70'); |
| # | Line 3285 sub _tree_construction_main ($) { | Line 3903 sub _tree_construction_main ($) { |
| 3903 | } | } |
| 3904 | }; # $insert_to_foster | }; # $insert_to_foster |
| 3905 | ||
| 3906 | B: { | B: while (1) { |
| 3907 | if ($token->{type} == DOCTYPE_TOKEN) { | if ($token->{type} == DOCTYPE_TOKEN) { |
| 3908 | !!!cp ('t73'); | !!!cp ('t73'); |
| 3909 | !!!parse-error (type => 'DOCTYPE in the middle'); | !!!parse-error (type => 'DOCTYPE in the middle', token => $token); |
| 3910 | ## Ignore the token | ## Ignore the token |
| 3911 | ## Stay in the phase | ## Stay in the phase |
| 3912 | !!!next-token; | !!!next-token; |
| 3913 | redo B; | next B; |
| 3914 | } elsif ($token->{type} == START_TAG_TOKEN and | } elsif ($token->{type} == START_TAG_TOKEN and |
| 3915 | $token->{tag_name} eq 'html') { | $token->{tag_name} eq 'html') { |
| 3916 | if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) { | if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) { |
| 3917 | !!!cp ('t79'); | !!!cp ('t79'); |
| 3918 | !!!parse-error (type => 'after html:html'); | !!!parse-error (type => 'after html:html', token => $token); |
| 3919 | $self->{insertion_mode} = AFTER_BODY_IM; | $self->{insertion_mode} = AFTER_BODY_IM; |
| 3920 | } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) { | } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) { |
| 3921 | !!!cp ('t80'); | !!!cp ('t80'); |
| 3922 | !!!parse-error (type => 'after html:html'); | !!!parse-error (type => 'after html:html', token => $token); |
| 3923 | $self->{insertion_mode} = AFTER_FRAMESET_IM; | $self->{insertion_mode} = AFTER_FRAMESET_IM; |
| 3924 | } else { | } else { |
| 3925 | !!!cp ('t81'); | !!!cp ('t81'); |
| 3926 | } | } |
| 3927 | ||
| 3928 | !!!cp ('t82'); | !!!cp ('t82'); |
| 3929 | !!!parse-error (type => 'not first start tag'); | !!!parse-error (type => 'not first start tag', token => $token); |
| 3930 | my $top_el = $self->{open_elements}->[0]->[0]; | my $top_el = $self->{open_elements}->[0]->[0]; |
| 3931 | for my $attr_name (keys %{$token->{attributes}}) { | for my $attr_name (keys %{$token->{attributes}}) { |
| 3932 | unless ($top_el->has_attribute_ns (undef, $attr_name)) { | unless ($top_el->has_attribute_ns (undef, $attr_name)) { |
| # | Line 3318 sub _tree_construction_main ($) { | Line 3936 sub _tree_construction_main ($) { |
| 3936 | $token->{attributes}->{$attr_name}->{value}); | $token->{attributes}->{$attr_name}->{value}); |
| 3937 | } | } |
| 3938 | } | } |
| 3939 | !!!nack ('t84.1'); | |
| 3940 | !!!next-token; | !!!next-token; |
| 3941 | redo B; | next B; |
| 3942 | } elsif ($token->{type} == COMMENT_TOKEN) { | } elsif ($token->{type} == COMMENT_TOKEN) { |
| 3943 | my $comment = $self->{document}->create_comment ($token->{data}); | my $comment = $self->{document}->create_comment ($token->{data}); |
| 3944 | if ($self->{insertion_mode} & AFTER_HTML_IMS) { | if ($self->{insertion_mode} & AFTER_HTML_IMS) { |
| # | Line 3333 sub _tree_construction_main ($) { | Line 3952 sub _tree_construction_main ($) { |
| 3952 | $self->{open_elements}->[-1]->[0]->append_child ($comment); | $self->{open_elements}->[-1]->[0]->append_child ($comment); |
| 3953 | } | } |
| 3954 | !!!next-token; | !!!next-token; |
| 3955 | redo B; | next B; |
| 3956 | } elsif ($self->{insertion_mode} & HEAD_IMS) { | } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) { |
| 3957 | if ($token->{type} == CHARACTER_TOKEN) { | |
| 3958 | !!!cp ('t87.1'); | |
| 3959 | $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data}); | |
| 3960 | !!!next-token; | |
| 3961 | next B; | |
| 3962 | } elsif ($token->{type} == START_TAG_TOKEN) { | |
| 3963 | if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and | |
| 3964 | $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or | |
| 3965 | not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or | |
| 3966 | ($token->{tag_name} eq 'svg' and | |
| 3967 | $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) { | |
| 3968 | ## NOTE: "using the rules for secondary insertion mode"then"continue" | |
| 3969 | !!!cp ('t87.2'); | |
| 3970 | # | |
| 3971 | } elsif ({ | |
| 3972 | b => 1, big => 1, blockquote => 1, body => 1, br => 1, | |
| 3973 | center => 1, code => 1, dd => 1, div => 1, dl => 1, em => 1, | |
| 3974 | embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1, ## No h4! | |
| 3975 | h5 => 1, h6 => 1, head => 1, hr => 1, i => 1, img => 1, | |
| 3976 | li => 1, menu => 1, meta => 1, nobr => 1, p => 1, pre => 1, | |
| 3977 | ruby => 1, s => 1, small => 1, span => 1, strong => 1, | |
| 3978 | sub => 1, sup => 1, table => 1, tt => 1, u => 1, ul => 1, | |
| 3979 | var => 1, | |
| 3980 | }->{$token->{tag_name}}) { | |
| 3981 | !!!cp ('t87.2'); | |
| 3982 | !!!parse-error (type => 'not closed', | |
| 3983 | value => $self->{open_elements}->[-1]->[0] | |
| 3984 | ->manakai_local_name, | |
| 3985 | token => $token); | |
| 3986 | ||
| 3987 | pop @{$self->{open_elements}} | |
| 3988 | while $self->{open_elements}->[-1]->[1] & FOREIGN_EL; | |
| 3989 | ||
| 3990 | $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM; | |
| 3991 | ## Reprocess. | |
| 3992 | next B; | |
| 3993 | } else { | |
| 3994 | my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri; | |
| 3995 | my $tag_name = $token->{tag_name}; | |
| 3996 | if ($nsuri eq $SVG_NS) { | |
| 3997 | $tag_name = { | |
| 3998 | altglyph => 'altGlyph', | |
| 3999 | altglyphdef => 'altGlyphDef', | |
| 4000 | altglyphitem => 'altGlyphItem', | |
| 4001 | animatecolor => 'animateColor', | |
| 4002 | animatemotion => 'animateMotion', | |
| 4003 | animatetransform => 'animateTransform', | |
| 4004 | clippath => 'clipPath', | |
| 4005 | feblend => 'feBlend', | |
| 4006 | fecolormatrix => 'feColorMatrix', | |
| 4007 | fecomponenttransfer => 'feComponentTransfer', | |
| 4008 | fecomposite => 'feComposite', | |
| 4009 | feconvolvematrix => 'feConvolveMatrix', | |
| 4010 | fediffuselighting => 'feDiffuseLighting', | |
| 4011 | fedisplacementmap => 'feDisplacementMap', | |
| 4012 | fedistantlight => 'feDistantLight', | |
| 4013 | feflood => 'feFlood', | |
| 4014 | fefunca => 'feFuncA', | |
| 4015 | fefuncb => 'feFuncB', | |
| 4016 | fefuncg => 'feFuncG', | |
| 4017 | fefuncr => 'feFuncR', | |
| 4018 | fegaussianblur => 'feGaussianBlur', | |
| 4019 | feimage => 'feImage', | |
| 4020 | femerge => 'feMerge', | |
| 4021 | femergenode => 'feMergeNode', | |
| 4022 | femorphology => 'feMorphology', | |
| 4023 | feoffset => 'feOffset', | |
| 4024 | fepointlight => 'fePointLight', | |
| 4025 | fespecularlighting => 'feSpecularLighting', | |
| 4026 | fespotlight => 'feSpotLight', | |
| 4027 | fetile => 'feTile', | |
| 4028 | feturbulence => 'feTurbulence', | |
| 4029 | foreignobject => 'foreignObject', | |
| 4030 | glyphref => 'glyphRef', | |
| 4031 | lineargradient => 'linearGradient', | |
| 4032 | radialgradient => 'radialGradient', | |
| 4033 | #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2) | |
| 4034 | textpath => 'textPath', | |
| 4035 | }->{$tag_name} || $tag_name; | |
| 4036 | } | |
| 4037 | ||
| 4038 | ## "adjust SVG attributes" (SVG only) - done in insert-element-f | |
| 4039 | ||
| 4040 | ## "adjust foreign attributes" - done in insert-element-f | |
| 4041 | ||
| 4042 | !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token); | |
| 4043 | ||
| 4044 | if ($self->{self_closing}) { | |
| 4045 | pop @{$self->{open_elements}}; | |
| 4046 | !!!ack ('t87.3'); | |
| 4047 | } else { | |
| 4048 | !!!cp ('t87.4'); | |
| 4049 | } | |
| 4050 | ||
| 4051 | !!!next-token; | |
| 4052 | next B; | |
| 4053 | } | |
| 4054 | } elsif ($token->{type} == END_TAG_TOKEN) { | |
| 4055 | ## NOTE: "using the rules for secondary insertion mode" then "continue" | |
| 4056 | !!!cp ('t87.5'); | |
| 4057 | # | |
| 4058 | } elsif ($token->{type} == END_OF_FILE_TOKEN) { | |
| 4059 | ## NOTE: "using the rules for secondary insertion mode" then "continue" | |
| 4060 | !!!cp ('t87.6'); | |
| 4061 | # | |
| 4062 | ## TODO: ... | |
| 4063 | } else { | |
| 4064 | die "$0: $token->{type}: Unknown token type"; | |
| 4065 | } | |
| 4066 | } | |
| 4067 | ||
| 4068 | if ($self->{insertion_mode} & HEAD_IMS) { | |
| 4069 | if ($token->{type} == CHARACTER_TOKEN) { | if ($token->{type} == CHARACTER_TOKEN) { |
| 4070 | if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { | if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { |
| 4071 | unless ($self->{insertion_mode} == BEFORE_HEAD_IM) { | unless ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| # | Line 3344 sub _tree_construction_main ($) { | Line 4075 sub _tree_construction_main ($) { |
| 4075 | !!!cp ('t88.1'); | !!!cp ('t88.1'); |
| 4076 | ## Ignore the token. | ## Ignore the token. |
| 4077 | !!!next-token; | !!!next-token; |
| 4078 | redo B; | next B; |
| 4079 | } | } |
| 4080 | unless (length $token->{data}) { | unless (length $token->{data}) { |
| 4081 | !!!cp ('t88'); | !!!cp ('t88'); |
| 4082 | !!!next-token; | !!!next-token; |
| 4083 | redo B; | next B; |
| 4084 | } | } |
| 4085 | } | } |
| 4086 | ||
| 4087 | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4088 | !!!cp ('t89'); | !!!cp ('t89'); |
| 4089 | ## As if <head> | ## As if <head> |
| 4090 | !!!create-element ($self->{head_element}, 'head'); | !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token); |
| 4091 | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); |
| 4092 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4093 | [$self->{head_element}, $el_category->{head}]; | |
| 4094 | ||
| 4095 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4096 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 3368 sub _tree_construction_main ($) { | Line 4100 sub _tree_construction_main ($) { |
| 4100 | !!!cp ('t90'); | !!!cp ('t90'); |
| 4101 | ## As if </noscript> | ## As if </noscript> |
| 4102 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4103 | !!!parse-error (type => 'in noscript:#character'); | !!!parse-error (type => 'in noscript:#character', token => $token); |
| 4104 | ||
| 4105 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4106 | ## As if </head> | ## As if </head> |
| # | Line 3384 sub _tree_construction_main ($) { | Line 4116 sub _tree_construction_main ($) { |
| 4116 | !!!cp ('t92'); | !!!cp ('t92'); |
| 4117 | } | } |
| 4118 | ||
| 4119 | ## "after head" insertion mode | ## "after head" insertion mode |
| 4120 | ## As if <body> | ## As if <body> |
| 4121 | !!!insert-element ('body'); | !!!insert-element ('body',, $token); |
| 4122 | $self->{insertion_mode} = IN_BODY_IM; | $self->{insertion_mode} = IN_BODY_IM; |
| 4123 | ## reprocess | ## reprocess |
| 4124 | redo B; | next B; |
| 4125 | } elsif ($token->{type} == START_TAG_TOKEN) { | } elsif ($token->{type} == START_TAG_TOKEN) { |
| 4126 | if ($token->{tag_name} eq 'head') { | if ($token->{tag_name} eq 'head') { |
| 4127 | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4128 | !!!cp ('t93'); | !!!cp ('t93'); |
| 4129 | !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes}); | !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token); |
| 4130 | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); | $self->{open_elements}->[-1]->[0]->append_child |
| 4131 | push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}]; | ($self->{head_element}); |
| 4132 | $self->{insertion_mode} = IN_HEAD_IM; | push @{$self->{open_elements}}, |
| 4133 | !!!next-token; | [$self->{head_element}, $el_category->{head}]; |
| 4134 | redo B; | $self->{insertion_mode} = IN_HEAD_IM; |
| 4135 | } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) { | !!!nack ('t93.1'); |
| 4136 | !!!cp ('t94'); | !!!next-token; |
| 4137 | # | next B; |
| 4138 | } else { | } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) { |
| 4139 | !!!cp ('t95'); | !!!cp ('t94'); |
| 4140 | !!!parse-error (type => 'in head:head'); # or in head noscript | # |
| 4141 | ## Ignore the token | } else { |
| 4142 | !!!next-token; | !!!cp ('t95'); |
| 4143 | redo B; | !!!parse-error (type => 'in head:head', token => $token); # or in head noscript |
| 4144 | } | ## Ignore the token |
| 4145 | } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) { | !!!nack ('t95.1'); |
| 4146 | !!!cp ('t96'); | !!!next-token; |
| 4147 | ## As if <head> | next B; |
| 4148 | !!!create-element ($self->{head_element}, 'head'); | } |
| 4149 | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); | } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4150 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | !!!cp ('t96'); |
| 4151 | ## As if <head> | |
| 4152 | !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token); | |
| 4153 | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); | |
| 4154 | push @{$self->{open_elements}}, | |
| 4155 | [$self->{head_element}, $el_category->{head}]; | |
| 4156 | ||
| 4157 | $self->{insertion_mode} = IN_HEAD_IM; | $self->{insertion_mode} = IN_HEAD_IM; |
| 4158 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4159 | } else { | } else { |
| 4160 | !!!cp ('t97'); | !!!cp ('t97'); |
| 4161 | } | } |
| 4162 | ||
| 4163 | if ($token->{tag_name} eq 'base') { | if ($token->{tag_name} eq 'base') { |
| 4164 | if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { | if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { |
| 4165 | !!!cp ('t98'); | !!!cp ('t98'); |
| 4166 | ## As if </noscript> | ## As if </noscript> |
| 4167 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4168 | !!!parse-error (type => 'in noscript:base'); | !!!parse-error (type => 'in noscript:base', token => $token); |
| 4169 | ||
| 4170 | $self->{insertion_mode} = IN_HEAD_IM; | $self->{insertion_mode} = IN_HEAD_IM; |
| 4171 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| # | Line 3439 sub _tree_construction_main ($) { | Line 4176 sub _tree_construction_main ($) { |
| 4176 | ## NOTE: There is a "as if in head" code clone. | ## NOTE: There is a "as if in head" code clone. |
| 4177 | if ($self->{insertion_mode} == AFTER_HEAD_IM) { | if ($self->{insertion_mode} == AFTER_HEAD_IM) { |
| 4178 | !!!cp ('t100'); | !!!cp ('t100'); |
| 4179 | !!!parse-error (type => 'after head:'.$token->{tag_name}); | !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token); |
| 4180 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4181 | [$self->{head_element}, $el_category->{head}]; | |
| 4182 | } else { | } else { |
| 4183 | !!!cp ('t101'); | !!!cp ('t101'); |
| 4184 | } | } |
| 4185 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 4186 | pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec. | pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec. |
| 4187 | pop @{$self->{open_elements}} # <head> | pop @{$self->{open_elements}} # <head> |
| 4188 | if $self->{insertion_mode} == AFTER_HEAD_IM; | if $self->{insertion_mode} == AFTER_HEAD_IM; |
| 4189 | !!!nack ('t101.1'); | |
| 4190 | !!!next-token; | !!!next-token; |
| 4191 | redo B; | next B; |
| 4192 | } elsif ($token->{tag_name} eq 'link') { | } elsif ($token->{tag_name} eq 'link') { |
| 4193 | ## NOTE: There is a "as if in head" code clone. | ## NOTE: There is a "as if in head" code clone. |
| 4194 | if ($self->{insertion_mode} == AFTER_HEAD_IM) { | if ($self->{insertion_mode} == AFTER_HEAD_IM) { |
| 4195 | !!!cp ('t102'); | !!!cp ('t102'); |
| 4196 | !!!parse-error (type => 'after head:'.$token->{tag_name}); | !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token); |
| 4197 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4198 | [$self->{head_element}, $el_category->{head}]; | |
| 4199 | } else { | } else { |
| 4200 | !!!cp ('t103'); | !!!cp ('t103'); |
| 4201 | } | } |
| 4202 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 4203 | pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec. | pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec. |
| 4204 | pop @{$self->{open_elements}} # <head> | pop @{$self->{open_elements}} # <head> |
| 4205 | if $self->{insertion_mode} == AFTER_HEAD_IM; | if $self->{insertion_mode} == AFTER_HEAD_IM; |
| 4206 | !!!ack ('t103.1'); | |
| 4207 | !!!next-token; | !!!next-token; |
| 4208 | redo B; | next B; |
| 4209 | } elsif ($token->{tag_name} eq 'meta') { | } elsif ($token->{tag_name} eq 'meta') { |
| 4210 | ## NOTE: There is a "as if in head" code clone. | ## NOTE: There is a "as if in head" code clone. |
| 4211 | if ($self->{insertion_mode} == AFTER_HEAD_IM) { | if ($self->{insertion_mode} == AFTER_HEAD_IM) { |
| 4212 | !!!cp ('t104'); | !!!cp ('t104'); |
| 4213 | !!!parse-error (type => 'after head:'.$token->{tag_name}); | !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token); |
| 4214 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4215 | [$self->{head_element}, $el_category->{head}]; | |
| 4216 | } else { | } else { |
| 4217 | !!!cp ('t105'); | !!!cp ('t105'); |
| 4218 | } | } |
| 4219 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 4220 | my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec. | my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec. |
| 4221 | ||
| 4222 | unless ($self->{confident}) { | unless ($self->{confident}) { |
| 4223 | if ($token->{attributes}->{charset}) { ## TODO: And if supported | if ($token->{attributes}->{charset}) { |
| 4224 | !!!cp ('t106'); | !!!cp ('t106'); |
| 4225 | ## NOTE: Whether the encoding is supported or not is handled | |
| 4226 | ## in the {change_encoding} callback. | |
| 4227 | $self->{change_encoding} | $self->{change_encoding} |
| 4228 | ->($self, $token->{attributes}->{charset}->{value}); | ->($self, $token->{attributes}->{charset}->{value}, |
| 4229 | $token); | |
| 4230 | ||
| 4231 | $meta_el->[0]->get_attribute_node_ns (undef, 'charset') | $meta_el->[0]->get_attribute_node_ns (undef, 'charset') |
| 4232 | ->set_user_data (manakai_has_reference => | ->set_user_data (manakai_has_reference => |
| 4233 | $token->{attributes}->{charset} | $token->{attributes}->{charset} |
| 4234 | ->{has_reference}); | ->{has_reference}); |
| 4235 | } elsif ($token->{attributes}->{content}) { | } elsif ($token->{attributes}->{content}) { |
| ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition. | ||
| 4236 | if ($token->{attributes}->{content}->{value} | if ($token->{attributes}->{content}->{value} |
| 4237 | =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt] | =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt] |
| 4238 | [\x09-\x0D\x20]*= | [\x09-\x0D\x20]*= |
| 4239 | [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'| | [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'| |
| 4240 | ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) { | ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) { |
| 4241 | !!!cp ('t107'); | !!!cp ('t107'); |
| 4242 | ## NOTE: Whether the encoding is supported or not is handled | |
| 4243 | ## in the {change_encoding} callback. | |
| 4244 | $self->{change_encoding} | $self->{change_encoding} |
| 4245 | ->($self, defined $1 ? $1 : defined $2 ? $2 : $3); | ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, |
| 4246 | $token); | |
| 4247 | $meta_el->[0]->get_attribute_node_ns (undef, 'content') | $meta_el->[0]->get_attribute_node_ns (undef, 'content') |
| 4248 | ->set_user_data (manakai_has_reference => | ->set_user_data (manakai_has_reference => |
| 4249 | $token->{attributes}->{content} | $token->{attributes}->{content} |
| # | Line 3524 sub _tree_construction_main ($) { | Line 4271 sub _tree_construction_main ($) { |
| 4271 | ||
| 4272 | pop @{$self->{open_elements}} # <head> | pop @{$self->{open_elements}} # <head> |
| 4273 | if $self->{insertion_mode} == AFTER_HEAD_IM; | if $self->{insertion_mode} == AFTER_HEAD_IM; |
| 4274 | !!!ack ('t110.1'); | |
| 4275 | !!!next-token; | !!!next-token; |
| 4276 | redo B; | next B; |
| 4277 | } elsif ($token->{tag_name} eq 'title') { | } elsif ($token->{tag_name} eq 'title') { |
| 4278 | if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { | if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { |
| 4279 | !!!cp ('t111'); | !!!cp ('t111'); |
| 4280 | ## As if </noscript> | ## As if </noscript> |
| 4281 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4282 | !!!parse-error (type => 'in noscript:title'); | !!!parse-error (type => 'in noscript:title', token => $token); |
| 4283 | ||
| 4284 | $self->{insertion_mode} = IN_HEAD_IM; | $self->{insertion_mode} = IN_HEAD_IM; |
| 4285 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4286 | } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) { | } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) { |
| 4287 | !!!cp ('t112'); | !!!cp ('t112'); |
| 4288 | !!!parse-error (type => 'after head:'.$token->{tag_name}); | !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token); |
| 4289 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4290 | [$self->{head_element}, $el_category->{head}]; | |
| 4291 | } else { | } else { |
| 4292 | !!!cp ('t113'); | !!!cp ('t113'); |
| 4293 | } | } |
| # | Line 3549 sub _tree_construction_main ($) { | Line 4298 sub _tree_construction_main ($) { |
| 4298 | $parse_rcdata->(RCDATA_CONTENT_MODEL); | $parse_rcdata->(RCDATA_CONTENT_MODEL); |
| 4299 | pop @{$self->{open_elements}} # <head> | pop @{$self->{open_elements}} # <head> |
| 4300 | if $self->{insertion_mode} == AFTER_HEAD_IM; | if $self->{insertion_mode} == AFTER_HEAD_IM; |
| 4301 | redo B; | next B; |
| 4302 | } elsif ($token->{tag_name} eq 'style') { | } elsif ($token->{tag_name} eq 'style') { |
| 4303 | ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and | ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and |
| 4304 | ## insertion mode IN_HEAD_IM) | ## insertion mode IN_HEAD_IM) |
| 4305 | ## NOTE: There is a "as if in head" code clone. | ## NOTE: There is a "as if in head" code clone. |
| 4306 | if ($self->{insertion_mode} == AFTER_HEAD_IM) { | if ($self->{insertion_mode} == AFTER_HEAD_IM) { |
| 4307 | !!!cp ('t114'); | !!!cp ('t114'); |
| 4308 | !!!parse-error (type => 'after head:'.$token->{tag_name}); | !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token); |
| 4309 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4310 | [$self->{head_element}, $el_category->{head}]; | |
| 4311 | } else { | } else { |
| 4312 | !!!cp ('t115'); | !!!cp ('t115'); |
| 4313 | } | } |
| 4314 | $parse_rcdata->(CDATA_CONTENT_MODEL); | $parse_rcdata->(CDATA_CONTENT_MODEL); |
| 4315 | pop @{$self->{open_elements}} # <head> | pop @{$self->{open_elements}} # <head> |
| 4316 | if $self->{insertion_mode} == AFTER_HEAD_IM; | if $self->{insertion_mode} == AFTER_HEAD_IM; |
| 4317 | redo B; | next B; |
| 4318 | } elsif ($token->{tag_name} eq 'noscript') { | } elsif ($token->{tag_name} eq 'noscript') { |
| 4319 | if ($self->{insertion_mode} == IN_HEAD_IM) { | if ($self->{insertion_mode} == IN_HEAD_IM) { |
| 4320 | !!!cp ('t116'); | !!!cp ('t116'); |
| 4321 | ## NOTE: and scripting is disalbed | ## NOTE: and scripting is disalbed |
| 4322 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 4323 | $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM; | $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM; |
| 4324 | !!!nack ('t116.1'); | |
| 4325 | !!!next-token; | !!!next-token; |
| 4326 | redo B; | next B; |
| 4327 | } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { | } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { |
| 4328 | !!!cp ('t117'); | !!!cp ('t117'); |
| 4329 | !!!parse-error (type => 'in noscript:noscript'); | !!!parse-error (type => 'in noscript:noscript', token => $token); |
| 4330 | ## Ignore the token | ## Ignore the token |
| 4331 | !!!nack ('t117.1'); | |
| 4332 | !!!next-token; | !!!next-token; |
| 4333 | redo B; | next B; |
| 4334 | } else { | } else { |
| 4335 | !!!cp ('t118'); | !!!cp ('t118'); |
| 4336 | # | # |
| # | Line 3588 sub _tree_construction_main ($) { | Line 4340 sub _tree_construction_main ($) { |
| 4340 | !!!cp ('t119'); | !!!cp ('t119'); |
| 4341 | ## As if </noscript> | ## As if </noscript> |
| 4342 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4343 | !!!parse-error (type => 'in noscript:script'); | !!!parse-error (type => 'in noscript:script', token => $token); |
| 4344 | ||
| 4345 | $self->{insertion_mode} = IN_HEAD_IM; | $self->{insertion_mode} = IN_HEAD_IM; |
| 4346 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4347 | } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) { | } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) { |
| 4348 | !!!cp ('t120'); | !!!cp ('t120'); |
| 4349 | !!!parse-error (type => 'after head:'.$token->{tag_name}); | !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token); |
| 4350 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4351 | [$self->{head_element}, $el_category->{head}]; | |
| 4352 | } else { | } else { |
| 4353 | !!!cp ('t121'); | !!!cp ('t121'); |
| 4354 | } | } |
| # | Line 3604 sub _tree_construction_main ($) { | Line 4357 sub _tree_construction_main ($) { |
| 4357 | $script_start_tag->(); | $script_start_tag->(); |
| 4358 | pop @{$self->{open_elements}} # <head> | pop @{$self->{open_elements}} # <head> |
| 4359 | if $self->{insertion_mode} == AFTER_HEAD_IM; | if $self->{insertion_mode} == AFTER_HEAD_IM; |
| 4360 | redo B; | next B; |
| 4361 | } elsif ($token->{tag_name} eq 'body' or | } elsif ($token->{tag_name} eq 'body' or |
| 4362 | $token->{tag_name} eq 'frameset') { | $token->{tag_name} eq 'frameset') { |
| 4363 | if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { | if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { |
| 4364 | !!!cp ('t122'); | !!!cp ('t122'); |
| 4365 | ## As if </noscript> | ## As if </noscript> |
| 4366 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4367 | !!!parse-error (type => 'in noscript:'.$token->{tag_name}); | !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token); |
| 4368 | ||
| 4369 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4370 | ## As if </head> | ## As if </head> |
| # | Line 3628 sub _tree_construction_main ($) { | Line 4381 sub _tree_construction_main ($) { |
| 4381 | } | } |
| 4382 | ||
| 4383 | ## "after head" insertion mode | ## "after head" insertion mode |
| 4384 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 4385 | if ($token->{tag_name} eq 'body') { | if ($token->{tag_name} eq 'body') { |
| 4386 | !!!cp ('t126'); | !!!cp ('t126'); |
| 4387 | $self->{insertion_mode} = IN_BODY_IM; | $self->{insertion_mode} = IN_BODY_IM; |
| # | Line 3638 sub _tree_construction_main ($) { | Line 4391 sub _tree_construction_main ($) { |
| 4391 | } else { | } else { |
| 4392 | die "$0: tag name: $self->{tag_name}"; | die "$0: tag name: $self->{tag_name}"; |
| 4393 | } | } |
| 4394 | !!!nack ('t127.1'); | |
| 4395 | !!!next-token; | !!!next-token; |
| 4396 | redo B; | next B; |
| 4397 | } else { | } else { |
| 4398 | !!!cp ('t128'); | !!!cp ('t128'); |
| 4399 | # | # |
| # | Line 3649 sub _tree_construction_main ($) { | Line 4403 sub _tree_construction_main ($) { |
| 4403 | !!!cp ('t129'); | !!!cp ('t129'); |
| 4404 | ## As if </noscript> | ## As if </noscript> |
| 4405 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4406 | !!!parse-error (type => 'in noscript:/'.$token->{tag_name}); | !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token); |
| 4407 | ||
| 4408 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4409 | ## As if </head> | ## As if </head> |
| # | Line 3668 sub _tree_construction_main ($) { | Line 4422 sub _tree_construction_main ($) { |
| 4422 | ||
| 4423 | ## "after head" insertion mode | ## "after head" insertion mode |
| 4424 | ## As if <body> | ## As if <body> |
| 4425 | !!!insert-element ('body'); | !!!insert-element ('body',, $token); |
| 4426 | $self->{insertion_mode} = IN_BODY_IM; | $self->{insertion_mode} = IN_BODY_IM; |
| 4427 | ## reprocess | ## reprocess |
| 4428 | redo B; | !!!ack-later; |
| 4429 | next B; | |
| 4430 | } elsif ($token->{type} == END_TAG_TOKEN) { | } elsif ($token->{type} == END_TAG_TOKEN) { |
| 4431 | if ($token->{tag_name} eq 'head') { | if ($token->{tag_name} eq 'head') { |
| 4432 | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4433 | !!!cp ('t132'); | !!!cp ('t132'); |
| 4434 | ## As if <head> | ## As if <head> |
| 4435 | !!!create-element ($self->{head_element}, 'head'); | !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token); |
| 4436 | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); |
| 4437 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4438 | [$self->{head_element}, $el_category->{head}]; | |
| 4439 | ||
| 4440 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4441 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4442 | $self->{insertion_mode} = AFTER_HEAD_IM; | $self->{insertion_mode} = AFTER_HEAD_IM; |
| 4443 | !!!next-token; | !!!next-token; |
| 4444 | redo B; | next B; |
| 4445 | } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { | } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { |
| 4446 | !!!cp ('t133'); | !!!cp ('t133'); |
| 4447 | ## As if </noscript> | ## As if </noscript> |
| 4448 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4449 | !!!parse-error (type => 'in noscript:/head'); | !!!parse-error (type => 'in noscript:/head', token => $token); |
| 4450 | ||
| 4451 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4452 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4453 | $self->{insertion_mode} = AFTER_HEAD_IM; | $self->{insertion_mode} = AFTER_HEAD_IM; |
| 4454 | !!!next-token; | !!!next-token; |
| 4455 | redo B; | next B; |
| 4456 | } elsif ($self->{insertion_mode} == IN_HEAD_IM) { | } elsif ($self->{insertion_mode} == IN_HEAD_IM) { |
| 4457 | !!!cp ('t134'); | !!!cp ('t134'); |
| 4458 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4459 | $self->{insertion_mode} = AFTER_HEAD_IM; | $self->{insertion_mode} = AFTER_HEAD_IM; |
| 4460 | !!!next-token; | !!!next-token; |
| 4461 | redo B; | next B; |
| 4462 | } else { | } else { |
| 4463 | !!!cp ('t135'); | !!!cp ('t135'); |
| 4464 | # | # |
| # | Line 3713 sub _tree_construction_main ($) { | Line 4469 sub _tree_construction_main ($) { |
| 4469 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4470 | $self->{insertion_mode} = IN_HEAD_IM; | $self->{insertion_mode} = IN_HEAD_IM; |
| 4471 | !!!next-token; | !!!next-token; |
| 4472 | redo B; | next B; |
| 4473 | } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) { | } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4474 | !!!cp ('t137'); | !!!cp ('t137'); |
| 4475 | !!!parse-error (type => 'unmatched end tag:noscript'); | !!!parse-error (type => 'unmatched end tag:noscript', token => $token); |
| 4476 | ## Ignore the token ## ISSUE: An issue in the spec. | ## Ignore the token ## ISSUE: An issue in the spec. |
| 4477 | !!!next-token; | !!!next-token; |
| 4478 | redo B; | next B; |
| 4479 | } else { | } else { |
| 4480 | !!!cp ('t138'); | !!!cp ('t138'); |
| 4481 | # | # |
| # | Line 3730 sub _tree_construction_main ($) { | Line 4486 sub _tree_construction_main ($) { |
| 4486 | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4487 | !!!cp ('t139'); | !!!cp ('t139'); |
| 4488 | ## As if <head> | ## As if <head> |
| 4489 | !!!create-element ($self->{head_element}, 'head'); | !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token); |
| 4490 | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); |
| 4491 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4492 | [$self->{head_element}, $el_category->{head}]; | |
| 4493 | ||
| 4494 | $self->{insertion_mode} = IN_HEAD_IM; | $self->{insertion_mode} = IN_HEAD_IM; |
| 4495 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4496 | } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { | } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { |
| 4497 | !!!cp ('t140'); | !!!cp ('t140'); |
| 4498 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 4499 | ## Ignore the token | ## Ignore the token |
| 4500 | !!!next-token; | !!!next-token; |
| 4501 | redo B; | next B; |
| 4502 | } else { | } else { |
| 4503 | !!!cp ('t141'); | !!!cp ('t141'); |
| 4504 | } | } |
| # | Line 3753 sub _tree_construction_main ($) { | Line 4510 sub _tree_construction_main ($) { |
| 4510 | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4511 | !!!cp ('t142'); | !!!cp ('t142'); |
| 4512 | ## As if <head> | ## As if <head> |
| 4513 | !!!create-element ($self->{head_element}, 'head'); | !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token); |
| 4514 | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); | $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element}); |
| 4515 | push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | push @{$self->{open_elements}}, |
| 4516 | [$self->{head_element}, $el_category->{head}]; | |
| 4517 | ||
| 4518 | $self->{insertion_mode} = IN_HEAD_IM; | $self->{insertion_mode} = IN_HEAD_IM; |
| 4519 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| # | Line 3770 sub _tree_construction_main ($) { | Line 4528 sub _tree_construction_main ($) { |
| 4528 | # | # |
| 4529 | } else { | } else { |
| 4530 | !!!cp ('t145'); | !!!cp ('t145'); |
| 4531 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 4532 | ## Ignore the token | ## Ignore the token |
| 4533 | !!!next-token; | !!!next-token; |
| 4534 | redo B; | next B; |
| 4535 | } | } |
| 4536 | } | } |
| 4537 | ||
| # | Line 3781 sub _tree_construction_main ($) { | Line 4539 sub _tree_construction_main ($) { |
| 4539 | !!!cp ('t146'); | !!!cp ('t146'); |
| 4540 | ## As if </noscript> | ## As if </noscript> |
| 4541 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4542 | !!!parse-error (type => 'in noscript:/'.$token->{tag_name}); | !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token); |
| 4543 | ||
| 4544 | ## Reprocess in the "in head" insertion mode... | ## Reprocess in the "in head" insertion mode... |
| 4545 | ## As if </head> | ## As if </head> |
| # | Line 3797 sub _tree_construction_main ($) { | Line 4555 sub _tree_construction_main ($) { |
| 4555 | } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) { | } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4556 | ## ISSUE: This case cannot be reached? | ## ISSUE: This case cannot be reached? |
| 4557 | !!!cp ('t148'); | !!!cp ('t148'); |
| 4558 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 4559 | ## Ignore the token ## ISSUE: An issue in the spec. | ## Ignore the token ## ISSUE: An issue in the spec. |
| 4560 | !!!next-token; | !!!next-token; |
| 4561 | redo B; | next B; |
| 4562 | } else { | } else { |
| 4563 | !!!cp ('t149'); | !!!cp ('t149'); |
| 4564 | } | } |
| 4565 | ||
| 4566 | ## "after head" insertion mode | ## "after head" insertion mode |
| 4567 | ## As if <body> | ## As if <body> |
| 4568 | !!!insert-element ('body'); | !!!insert-element ('body',, $token); |
| 4569 | $self->{insertion_mode} = IN_BODY_IM; | $self->{insertion_mode} = IN_BODY_IM; |
| 4570 | ## reprocess | ## reprocess |
| 4571 | redo B; | next B; |
| 4572 | } elsif ($token->{type} == END_OF_FILE_TOKEN) { | } elsif ($token->{type} == END_OF_FILE_TOKEN) { |
| 4573 | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { | if ($self->{insertion_mode} == BEFORE_HEAD_IM) { |
| 4574 | !!!cp ('t149.1'); | !!!cp ('t149.1'); |
| 4575 | ||
| 4576 | ## NOTE: As if <head> | ## NOTE: As if <head> |
| 4577 | !!!create-element ($self->{head_element}, 'head'); | !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token); |
| 4578 | $self->{open_elements}->[-1]->[0]->append_child | $self->{open_elements}->[-1]->[0]->append_child |
| 4579 | ($self->{head_element}); | ($self->{head_element}); |
| 4580 | #push @{$self->{open_elements}}, [$self->{head_element}, 'head']; | #push @{$self->{open_elements}}, |
| 4581 | # [$self->{head_element}, $el_category->{head}]; | |
| 4582 | #$self->{insertion_mode} = IN_HEAD_IM; | #$self->{insertion_mode} = IN_HEAD_IM; |
| 4583 | ## NOTE: Reprocess. | ## NOTE: Reprocess. |
| 4584 | ||
| # | Line 3841 sub _tree_construction_main ($) { | Line 4600 sub _tree_construction_main ($) { |
| 4600 | } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { | } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) { |
| 4601 | !!!cp ('t149.3'); | !!!cp ('t149.3'); |
| 4602 | ||
| 4603 | !!!parse-error (type => 'in noscript:#eof'); | !!!parse-error (type => 'in noscript:#eof', token => $token); |
| 4604 | ||
| 4605 | ## As if </noscript> | ## As if </noscript> |
| 4606 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 3860 sub _tree_construction_main ($) { | Line 4619 sub _tree_construction_main ($) { |
| 4619 | } | } |
| 4620 | ||
| 4621 | ## NOTE: As if <body> | ## NOTE: As if <body> |
| 4622 | !!!insert-element ('body'); | !!!insert-element ('body',, $token); |
| 4623 | $self->{insertion_mode} = IN_BODY_IM; | $self->{insertion_mode} = IN_BODY_IM; |
| 4624 | ## NOTE: Reprocess. | ## NOTE: Reprocess. |
| 4625 | redo B; | next B; |
| 4626 | } else { | } else { |
| 4627 | die "$0: $token->{type}: Unknown token type"; | die "$0: $token->{type}: Unknown token type"; |
| 4628 | } | } |
| # | Line 3878 sub _tree_construction_main ($) { | Line 4637 sub _tree_construction_main ($) { |
| 4637 | $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data}); | $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data}); |
| 4638 | ||
| 4639 | !!!next-token; | !!!next-token; |
| 4640 | redo B; | next B; |
| 4641 | } elsif ($token->{type} == START_TAG_TOKEN) { | } elsif ($token->{type} == START_TAG_TOKEN) { |
| 4642 | if ({ | if ({ |
| 4643 | caption => 1, col => 1, colgroup => 1, tbody => 1, | caption => 1, col => 1, colgroup => 1, tbody => 1, |
| # | Line 3886 sub _tree_construction_main ($) { | Line 4645 sub _tree_construction_main ($) { |
| 4645 | }->{$token->{tag_name}}) { | }->{$token->{tag_name}}) { |
| 4646 | if ($self->{insertion_mode} == IN_CELL_IM) { | if ($self->{insertion_mode} == IN_CELL_IM) { |
| 4647 | ## have an element in table scope | ## have an element in table scope |
| 4648 | my $tn; | for (reverse 0..$#{$self->{open_elements}}) { |
| INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | ||
| 4649 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 4650 | if ($node->[1] eq 'td' or $node->[1] eq 'th') { | if ($node->[1] & TABLE_CELL_EL) { |
| 4651 | !!!cp ('t151'); | !!!cp ('t151'); |
| 4652 | $tn = $node->[1]; | |
| 4653 | last INSCOPE; | ## Close the cell |
| 4654 | } elsif ({ | !!!back-token; # <x> |
| 4655 | table => 1, html => 1, | $token = {type => END_TAG_TOKEN, |
| 4656 | }->{$node->[1]}) { | tag_name => $node->[0]->manakai_local_name, |
| 4657 | line => $token->{line}, | |
| 4658 | column => $token->{column}}; | |
| 4659 | next B; | |
| 4660 | } elsif ($node->[1] & TABLE_SCOPING_EL) { | |
| 4661 | !!!cp ('t152'); | !!!cp ('t152'); |
| 4662 | last INSCOPE; | ## ISSUE: This case can never be reached, maybe. |
| 4663 | } | last; |
| } # INSCOPE | ||
| unless (defined $tn) { | ||
| !!!cp ('t153'); | ||
| ## TODO: This error type is wrong. | ||
| !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | ||
| ## Ignore the token | ||
| !!!next-token; | ||
| redo B; | ||
| 4664 | } | } |
| 4665 | } | |
| 4666 | !!!cp ('t154'); | |
| 4667 | ## Close the cell | !!!cp ('t153'); |
| 4668 | !!!back-token; # <?> | !!!parse-error (type => 'start tag not allowed', |
| 4669 | $token = {type => END_TAG_TOKEN, tag_name => $tn}; | value => $token->{tag_name}, token => $token); |
| 4670 | redo B; | ## Ignore the token |
| 4671 | !!!nack ('t153.1'); | |
| 4672 | !!!next-token; | |
| 4673 | next B; | |
| 4674 | } elsif ($self->{insertion_mode} == IN_CAPTION_IM) { | } elsif ($self->{insertion_mode} == IN_CAPTION_IM) { |
| 4675 | !!!parse-error (type => 'not closed:caption'); | !!!parse-error (type => 'not closed:caption', token => $token); |
| 4676 | ||
| 4677 | ## As if </caption> | ## NOTE: As if </caption>. |
| 4678 | ## have a table element in table scope | ## have a table element in table scope |
| 4679 | my $i; | my $i; |
| 4680 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: { |
| 4681 | my $node = $self->{open_elements}->[$_]; | for (reverse 0..$#{$self->{open_elements}}) { |
| 4682 | if ($node->[1] eq 'caption') { | my $node = $self->{open_elements}->[$_]; |
| 4683 | !!!cp ('t155'); | if ($node->[1] & CAPTION_EL) { |
| 4684 | $i = $_; | !!!cp ('t155'); |
| 4685 | last INSCOPE; | $i = $_; |
| 4686 | } elsif ({ | last INSCOPE; |
| 4687 | table => 1, html => 1, | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| 4688 | }->{$node->[1]}) { | !!!cp ('t156'); |
| 4689 | !!!cp ('t156'); | last; |
| 4690 | last INSCOPE; | } |
| 4691 | } | } |
| 4692 | ||
| 4693 | !!!cp ('t157'); | |
| 4694 | !!!parse-error (type => 'start tag not allowed', | |
| 4695 | value => $token->{tag_name}, token => $token); | |
| 4696 | ## Ignore the token | |
| 4697 | !!!nack ('t157.1'); | |
| 4698 | !!!next-token; | |
| 4699 | next B; | |
| 4700 | } # INSCOPE | } # INSCOPE |
| unless (defined $i) { | ||
| !!!cp ('t157'); | ||
| ## TODO: this type is wrong. | ||
| !!!parse-error (type => 'unmatched end tag:caption'); | ||
| ## Ignore the token | ||
| !!!next-token; | ||
| redo B; | ||
| } | ||
| 4701 | ||
| 4702 | ## generate implied end tags | ## generate implied end tags |
| 4703 | while ({ | while ($self->{open_elements}->[-1]->[1] |
| 4704 | dd => 1, dt => 1, li => 1, p => 1, | & END_TAG_OPTIONAL_EL) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 4705 | !!!cp ('t158'); | !!!cp ('t158'); |
| 4706 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4707 | } | } |
| 4708 | ||
| 4709 | if ($self->{open_elements}->[-1]->[1] ne 'caption') { | unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) { |
| 4710 | !!!cp ('t159'); | !!!cp ('t159'); |
| 4711 | !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]); | !!!parse-error (type => 'not closed', |
| 4712 | value => $self->{open_elements}->[-1]->[0] | |
| 4713 | ->manakai_local_name, | |
| 4714 | token => $token); | |
| 4715 | } else { | } else { |
| 4716 | !!!cp ('t160'); | !!!cp ('t160'); |
| 4717 | } | } |
| # | Line 3964 sub _tree_construction_main ($) { | Line 4723 sub _tree_construction_main ($) { |
| 4723 | $self->{insertion_mode} = IN_TABLE_IM; | $self->{insertion_mode} = IN_TABLE_IM; |
| 4724 | ||
| 4725 | ## reprocess | ## reprocess |
| 4726 | redo B; | !!!ack-later; |
| 4727 | next B; | |
| 4728 | } else { | } else { |
| 4729 | !!!cp ('t161'); | !!!cp ('t161'); |
| 4730 | # | # |
| # | Line 3980 sub _tree_construction_main ($) { | Line 4740 sub _tree_construction_main ($) { |
| 4740 | my $i; | my $i; |
| 4741 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 4742 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 4743 | if ($node->[1] eq $token->{tag_name}) { | if ($node->[0]->manakai_local_name eq $token->{tag_name}) { |
| 4744 | !!!cp ('t163'); | !!!cp ('t163'); |
| 4745 | $i = $_; | $i = $_; |
| 4746 | last INSCOPE; | last INSCOPE; |
| 4747 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 4748 | !!!cp ('t164'); | !!!cp ('t164'); |
| 4749 | last INSCOPE; | last INSCOPE; |
| 4750 | } | } |
| 4751 | } # INSCOPE | } # INSCOPE |
| 4752 | unless (defined $i) { | unless (defined $i) { |
| 4753 | !!!cp ('t165'); | !!!cp ('t165'); |
| 4754 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 4755 | ## Ignore the token | ## Ignore the token |
| 4756 | !!!next-token; | !!!next-token; |
| 4757 | redo B; | next B; |
| 4758 | } | } |
| 4759 | ||
| 4760 | ## generate implied end tags | ## generate implied end tags |
| 4761 | while ({ | while ($self->{open_elements}->[-1]->[1] |
| 4762 | dd => 1, dt => 1, li => 1, p => 1, | & END_TAG_OPTIONAL_EL) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 4763 | !!!cp ('t166'); | !!!cp ('t166'); |
| 4764 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4765 | } | } |
| 4766 | ||
| 4767 | if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) { | if ($self->{open_elements}->[-1]->[0]->manakai_local_name |
| 4768 | ne $token->{tag_name}) { | |
| 4769 | !!!cp ('t167'); | !!!cp ('t167'); |
| 4770 | !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]); | !!!parse-error (type => 'not closed', |
| 4771 | value => $self->{open_elements}->[-1]->[0] | |
| 4772 | ->manakai_local_name, | |
| 4773 | token => $token); | |
| 4774 | } else { | } else { |
| 4775 | !!!cp ('t168'); | !!!cp ('t168'); |
| 4776 | } | } |
| # | Line 4021 sub _tree_construction_main ($) { | Line 4782 sub _tree_construction_main ($) { |
| 4782 | $self->{insertion_mode} = IN_ROW_IM; | $self->{insertion_mode} = IN_ROW_IM; |
| 4783 | ||
| 4784 | !!!next-token; | !!!next-token; |
| 4785 | redo B; | next B; |
| 4786 | } elsif ($self->{insertion_mode} == IN_CAPTION_IM) { | } elsif ($self->{insertion_mode} == IN_CAPTION_IM) { |
| 4787 | !!!cp ('t169'); | !!!cp ('t169'); |
| 4788 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 4789 | ## Ignore the token | ## Ignore the token |
| 4790 | !!!next-token; | !!!next-token; |
| 4791 | redo B; | next B; |
| 4792 | } else { | } else { |
| 4793 | !!!cp ('t170'); | !!!cp ('t170'); |
| 4794 | # | # |
| # | Line 4036 sub _tree_construction_main ($) { | Line 4797 sub _tree_construction_main ($) { |
| 4797 | if ($self->{insertion_mode} == IN_CAPTION_IM) { | if ($self->{insertion_mode} == IN_CAPTION_IM) { |
| 4798 | ## have a table element in table scope | ## have a table element in table scope |
| 4799 | my $i; | my $i; |
| 4800 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: { |
| 4801 | my $node = $self->{open_elements}->[$_]; | for (reverse 0..$#{$self->{open_elements}}) { |
| 4802 | if ($node->[1] eq $token->{tag_name}) { | my $node = $self->{open_elements}->[$_]; |
| 4803 | !!!cp ('t171'); | if ($node->[1] & CAPTION_EL) { |
| 4804 | $i = $_; | !!!cp ('t171'); |
| 4805 | last INSCOPE; | $i = $_; |
| 4806 | } elsif ({ | last INSCOPE; |
| 4807 | table => 1, html => 1, | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| 4808 | }->{$node->[1]}) { | !!!cp ('t172'); |
| 4809 | !!!cp ('t172'); | last; |
| 4810 | last INSCOPE; | } |
| 4811 | } | } |
| 4812 | ||
| 4813 | !!!cp ('t173'); | |
| 4814 | !!!parse-error (type => 'unmatched end tag', | |
| 4815 | value => $token->{tag_name}, token => $token); | |
| 4816 | ## Ignore the token | |
| 4817 | !!!next-token; | |
| 4818 | next B; | |
| 4819 | } # INSCOPE | } # INSCOPE |
| unless (defined $i) { | ||
| !!!cp ('t173'); | ||
| !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | ||
| ## Ignore the token | ||
| !!!next-token; | ||
| redo B; | ||
| } | ||
| 4820 | ||
| 4821 | ## generate implied end tags | ## generate implied end tags |
| 4822 | while ({ | while ($self->{open_elements}->[-1]->[1] |
| 4823 | dd => 1, dt => 1, li => 1, p => 1, | & END_TAG_OPTIONAL_EL) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 4824 | !!!cp ('t174'); | !!!cp ('t174'); |
| 4825 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4826 | } | } |
| 4827 | ||
| 4828 | if ($self->{open_elements}->[-1]->[1] ne 'caption') { | unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) { |
| 4829 | !!!cp ('t175'); | !!!cp ('t175'); |
| 4830 | !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]); | !!!parse-error (type => 'not closed', |
| 4831 | value => $self->{open_elements}->[-1]->[0] | |
| 4832 | ->manakai_local_name, | |
| 4833 | token => $token); | |
| 4834 | } else { | } else { |
| 4835 | !!!cp ('t176'); | !!!cp ('t176'); |
| 4836 | } | } |
| # | Line 4079 sub _tree_construction_main ($) { | Line 4842 sub _tree_construction_main ($) { |
| 4842 | $self->{insertion_mode} = IN_TABLE_IM; | $self->{insertion_mode} = IN_TABLE_IM; |
| 4843 | ||
| 4844 | !!!next-token; | !!!next-token; |
| 4845 | redo B; | next B; |
| 4846 | } elsif ($self->{insertion_mode} == IN_CELL_IM) { | } elsif ($self->{insertion_mode} == IN_CELL_IM) { |
| 4847 | !!!cp ('t177'); | !!!cp ('t177'); |
| 4848 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 4849 | ## Ignore the token | ## Ignore the token |
| 4850 | !!!next-token; | !!!next-token; |
| 4851 | redo B; | next B; |
| 4852 | } else { | } else { |
| 4853 | !!!cp ('t178'); | !!!cp ('t178'); |
| 4854 | # | # |
| # | Line 4098 sub _tree_construction_main ($) { | Line 4861 sub _tree_construction_main ($) { |
| 4861 | ## have an element in table scope | ## have an element in table scope |
| 4862 | my $i; | my $i; |
| 4863 | my $tn; | my $tn; |
| 4864 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: { |
| 4865 | my $node = $self->{open_elements}->[$_]; | for (reverse 0..$#{$self->{open_elements}}) { |
| 4866 | if ($node->[1] eq $token->{tag_name}) { | my $node = $self->{open_elements}->[$_]; |
| 4867 | !!!cp ('t179'); | if ($node->[0]->manakai_local_name eq $token->{tag_name}) { |
| 4868 | $i = $_; | !!!cp ('t179'); |
| 4869 | last INSCOPE; | $i = $_; |
| 4870 | } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') { | |
| 4871 | !!!cp ('t180'); | ## Close the cell |
| 4872 | $tn = $node->[1]; | !!!back-token; # </x> |
| 4873 | ## NOTE: There is exactly one |td| or |th| element | $token = {type => END_TAG_TOKEN, tag_name => $tn, |
| 4874 | ## in scope in the stack of open elements by definition. | line => $token->{line}, |
| 4875 | } elsif ({ | column => $token->{column}}; |
| 4876 | table => 1, html => 1, | next B; |
| 4877 | }->{$node->[1]}) { | } elsif ($node->[1] & TABLE_CELL_EL) { |
| 4878 | !!!cp ('t181'); | !!!cp ('t180'); |
| 4879 | last INSCOPE; | $tn = $node->[0]->manakai_local_name; |
| 4880 | ## NOTE: There is exactly one |td| or |th| element | |
| 4881 | ## in scope in the stack of open elements by definition. | |
| 4882 | } elsif ($node->[1] & TABLE_SCOPING_EL) { | |
| 4883 | ## ISSUE: Can this be reached? | |
| 4884 | !!!cp ('t181'); | |
| 4885 | last; | |
| 4886 | } | |
| 4887 | } | } |
| 4888 | } # INSCOPE | |
| unless (defined $i) { | ||
| 4889 | !!!cp ('t182'); | !!!cp ('t182'); |
| 4890 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag', |
| 4891 | value => $token->{tag_name}, token => $token); | |
| 4892 | ## Ignore the token | ## Ignore the token |
| 4893 | !!!next-token; | !!!next-token; |
| 4894 | redo B; | next B; |
| 4895 | } else { | } # INSCOPE |
| !!!cp ('t183'); | ||
| } | ||
| ## Close the cell | ||
| !!!back-token; # </?> | ||
| $token = {type => END_TAG_TOKEN, tag_name => $tn}; | ||
| redo B; | ||
| 4896 | } elsif ($token->{tag_name} eq 'table' and | } elsif ($token->{tag_name} eq 'table' and |
| 4897 | $self->{insertion_mode} == IN_CAPTION_IM) { | $self->{insertion_mode} == IN_CAPTION_IM) { |
| 4898 | !!!parse-error (type => 'not closed:caption'); | !!!parse-error (type => 'not closed:caption', token => $token); |
| 4899 | ||
| 4900 | ## As if </caption> | ## As if </caption> |
| 4901 | ## have a table element in table scope | ## have a table element in table scope |
| 4902 | my $i; | my $i; |
| 4903 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 4904 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 4905 | if ($node->[1] eq 'caption') { | if ($node->[1] & CAPTION_EL) { |
| 4906 | !!!cp ('t184'); | !!!cp ('t184'); |
| 4907 | $i = $_; | $i = $_; |
| 4908 | last INSCOPE; | last INSCOPE; |
| 4909 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 4910 | !!!cp ('t185'); | !!!cp ('t185'); |
| 4911 | last INSCOPE; | last INSCOPE; |
| 4912 | } | } |
| 4913 | } # INSCOPE | } # INSCOPE |
| 4914 | unless (defined $i) { | unless (defined $i) { |
| 4915 | !!!cp ('t186'); | !!!cp ('t186'); |
| 4916 | !!!parse-error (type => 'unmatched end tag:caption'); | !!!parse-error (type => 'unmatched end tag:caption', token => $token); |
| 4917 | ## Ignore the token | ## Ignore the token |
| 4918 | !!!next-token; | !!!next-token; |
| 4919 | redo B; | next B; |
| 4920 | } | } |
| 4921 | ||
| 4922 | ## generate implied end tags | ## generate implied end tags |
| 4923 | while ({ | while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) { |
| dd => 1, dt => 1, li => 1, p => 1, | ||
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 4924 | !!!cp ('t187'); | !!!cp ('t187'); |
| 4925 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 4926 | } | } |
| 4927 | ||
| 4928 | if ($self->{open_elements}->[-1]->[1] ne 'caption') { | unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) { |
| 4929 | !!!cp ('t188'); | !!!cp ('t188'); |
| 4930 | !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]); | !!!parse-error (type => 'not closed', |
| 4931 | value => $self->{open_elements}->[-1]->[0] | |
| 4932 | ->manakai_local_name, | |
| 4933 | token => $token); | |
| 4934 | } else { | } else { |
| 4935 | !!!cp ('t189'); | !!!cp ('t189'); |
| 4936 | } | } |
| # | Line 4180 sub _tree_construction_main ($) { | Line 4942 sub _tree_construction_main ($) { |
| 4942 | $self->{insertion_mode} = IN_TABLE_IM; | $self->{insertion_mode} = IN_TABLE_IM; |
| 4943 | ||
| 4944 | ## reprocess | ## reprocess |
| 4945 | redo B; | next B; |
| 4946 | } elsif ({ | } elsif ({ |
| 4947 | body => 1, col => 1, colgroup => 1, html => 1, | body => 1, col => 1, colgroup => 1, html => 1, |
| 4948 | }->{$token->{tag_name}}) { | }->{$token->{tag_name}}) { |
| 4949 | if ($self->{insertion_mode} & BODY_TABLE_IMS) { | if ($self->{insertion_mode} & BODY_TABLE_IMS) { |
| 4950 | !!!cp ('t190'); | !!!cp ('t190'); |
| 4951 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 4952 | ## Ignore the token | ## Ignore the token |
| 4953 | !!!next-token; | !!!next-token; |
| 4954 | redo B; | next B; |
| 4955 | } else { | } else { |
| 4956 | !!!cp ('t191'); | !!!cp ('t191'); |
| 4957 | # | # |
| # | Line 4200 sub _tree_construction_main ($) { | Line 4962 sub _tree_construction_main ($) { |
| 4962 | }->{$token->{tag_name}} and | }->{$token->{tag_name}} and |
| 4963 | $self->{insertion_mode} == IN_CAPTION_IM) { | $self->{insertion_mode} == IN_CAPTION_IM) { |
| 4964 | !!!cp ('t192'); | !!!cp ('t192'); |
| 4965 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 4966 | ## Ignore the token | ## Ignore the token |
| 4967 | !!!next-token; | !!!next-token; |
| 4968 | redo B; | next B; |
| 4969 | } else { | } else { |
| 4970 | !!!cp ('t193'); | !!!cp ('t193'); |
| 4971 | # | # |
| 4972 | } | } |
| 4973 | } elsif ($token->{type} == END_OF_FILE_TOKEN) { | } elsif ($token->{type} == END_OF_FILE_TOKEN) { |
| 4974 | for my $entry (@{$self->{open_elements}}) { | for my $entry (@{$self->{open_elements}}) { |
| 4975 | if (not { | unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) { |
| dd => 1, dt => 1, li => 1, p => 1, tbody => 1, td => 1, tfoot => 1, | ||
| th => 1, thead => 1, tr => 1, body => 1, html => 1, | ||
| }->{$entry->[1]}) { | ||
| 4976 | !!!cp ('t75'); | !!!cp ('t75'); |
| 4977 | !!!parse-error (type => 'in body:#eof'); | !!!parse-error (type => 'in body:#eof', token => $token); |
| 4978 | last; | last; |
| 4979 | } | } |
| 4980 | } | } |
| # | Line 4237 sub _tree_construction_main ($) { | Line 4996 sub _tree_construction_main ($) { |
| 4996 | unless (length $token->{data}) { | unless (length $token->{data}) { |
| 4997 | !!!cp ('t194'); | !!!cp ('t194'); |
| 4998 | !!!next-token; | !!!next-token; |
| 4999 | redo B; | next B; |
| 5000 | } else { | } else { |
| 5001 | !!!cp ('t195'); | !!!cp ('t195'); |
| 5002 | } | } |
| 5003 | } | } |
| 5004 | ||
| 5005 | !!!parse-error (type => 'in table:#character'); | !!!parse-error (type => 'in table:#character', token => $token); |
| 5006 | ||
| 5007 | ## As if in body, but insert into foster parent element | ## As if in body, but insert into foster parent element |
| 5008 | ## ISSUE: Spec says that "whenever a node would be inserted | ## ISSUE: Spec says that "whenever a node would be inserted |
| # | Line 4251 sub _tree_construction_main ($) { | Line 5010 sub _tree_construction_main ($) { |
| 5010 | ## result in a new Text node. | ## result in a new Text node. |
| 5011 | $reconstruct_active_formatting_elements->($insert_to_foster); | $reconstruct_active_formatting_elements->($insert_to_foster); |
| 5012 | ||
| 5013 | if ({ | if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) { |
| table => 1, tbody => 1, tfoot => 1, | ||
| thead => 1, tr => 1, | ||
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5014 | # MUST | # MUST |
| 5015 | my $foster_parent_element; | my $foster_parent_element; |
| 5016 | my $next_sibling; | my $next_sibling; |
| 5017 | my $prev_sibling; | my $prev_sibling; |
| 5018 | OE: for (reverse 0..$#{$self->{open_elements}}) { | OE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5019 | if ($self->{open_elements}->[$_]->[1] eq 'table') { | if ($self->{open_elements}->[$_]->[1] & TABLE_EL) { |
| 5020 | my $parent = $self->{open_elements}->[$_]->[0]->parent_node; | my $parent = $self->{open_elements}->[$_]->[0]->parent_node; |
| 5021 | if (defined $parent and $parent->node_type == 1) { | if (defined $parent and $parent->node_type == 1) { |
| 5022 | !!!cp ('t196'); | !!!cp ('t196'); |
| # | Line 4295 sub _tree_construction_main ($) { | Line 5051 sub _tree_construction_main ($) { |
| 5051 | } | } |
| 5052 | ||
| 5053 | !!!next-token; | !!!next-token; |
| 5054 | redo B; | next B; |
| 5055 | } elsif ($token->{type} == START_TAG_TOKEN) { | } elsif ($token->{type} == START_TAG_TOKEN) { |
| 5056 | if ({ | if ({ |
| 5057 | tr => ($self->{insertion_mode} != IN_ROW_IM), | tr => ($self->{insertion_mode} != IN_ROW_IM), |
| # | Line 4303 sub _tree_construction_main ($) { | Line 5059 sub _tree_construction_main ($) { |
| 5059 | }->{$token->{tag_name}}) { | }->{$token->{tag_name}}) { |
| 5060 | if ($self->{insertion_mode} == IN_TABLE_IM) { | if ($self->{insertion_mode} == IN_TABLE_IM) { |
| 5061 | ## Clear back to table context | ## Clear back to table context |
| 5062 | while ($self->{open_elements}->[-1]->[1] ne 'table' and | while (not ($self->{open_elements}->[-1]->[1] |
| 5063 | $self->{open_elements}->[-1]->[1] ne 'html') { | & TABLE_SCOPING_EL)) { |
| 5064 | !!!cp ('t201'); | !!!cp ('t201'); |
| 5065 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5066 | } | } |
| 5067 | ||
| 5068 | !!!insert-element ('tbody'); | !!!insert-element ('tbody',, $token); |
| 5069 | $self->{insertion_mode} = IN_TABLE_BODY_IM; | $self->{insertion_mode} = IN_TABLE_BODY_IM; |
| 5070 | ## reprocess in the "in table body" insertion mode... | ## reprocess in the "in table body" insertion mode... |
| 5071 | } | } |
| # | Line 4317 sub _tree_construction_main ($) { | Line 5073 sub _tree_construction_main ($) { |
| 5073 | if ($self->{insertion_mode} == IN_TABLE_BODY_IM) { | if ($self->{insertion_mode} == IN_TABLE_BODY_IM) { |
| 5074 | unless ($token->{tag_name} eq 'tr') { | unless ($token->{tag_name} eq 'tr') { |
| 5075 | !!!cp ('t202'); | !!!cp ('t202'); |
| 5076 | !!!parse-error (type => 'missing start tag:tr'); | !!!parse-error (type => 'missing start tag:tr', token => $token); |
| 5077 | } | } |
| 5078 | ||
| 5079 | ## Clear back to table body context | ## Clear back to table body context |
| 5080 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5081 | tbody => 1, tfoot => 1, thead => 1, html => 1, | & TABLE_ROWS_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5082 | !!!cp ('t203'); | !!!cp ('t203'); |
| 5083 | ## ISSUE: Can this case be reached? | ## ISSUE: Can this case be reached? |
| 5084 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 4332 sub _tree_construction_main ($) { | Line 5087 sub _tree_construction_main ($) { |
| 5087 | $self->{insertion_mode} = IN_ROW_IM; | $self->{insertion_mode} = IN_ROW_IM; |
| 5088 | if ($token->{tag_name} eq 'tr') { | if ($token->{tag_name} eq 'tr') { |
| 5089 | !!!cp ('t204'); | !!!cp ('t204'); |
| 5090 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 5091 | !!!nack ('t204'); | |
| 5092 | !!!next-token; | !!!next-token; |
| 5093 | redo B; | next B; |
| 5094 | } else { | } else { |
| 5095 | !!!cp ('t205'); | !!!cp ('t205'); |
| 5096 | !!!insert-element ('tr'); | !!!insert-element ('tr',, $token); |
| 5097 | ## reprocess in the "in row" insertion mode | ## reprocess in the "in row" insertion mode |
| 5098 | } | } |
| 5099 | } else { | } else { |
| # | Line 4345 sub _tree_construction_main ($) { | Line 5101 sub _tree_construction_main ($) { |
| 5101 | } | } |
| 5102 | ||
| 5103 | ## Clear back to table row context | ## Clear back to table row context |
| 5104 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5105 | tr => 1, html => 1, | & TABLE_ROW_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5106 | !!!cp ('t207'); | !!!cp ('t207'); |
| 5107 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5108 | } | } |
| 5109 | ||
| 5110 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 5111 | $self->{insertion_mode} = IN_CELL_IM; | $self->{insertion_mode} = IN_CELL_IM; |
| 5112 | ||
| 5113 | push @$active_formatting_elements, ['#marker', '']; | push @$active_formatting_elements, ['#marker', '']; |
| 5114 | ||
| 5115 | !!!nack ('t207.1'); | |
| 5116 | !!!next-token; | !!!next-token; |
| 5117 | redo B; | next B; |
| 5118 | } elsif ({ | } elsif ({ |
| 5119 | caption => 1, col => 1, colgroup => 1, | caption => 1, col => 1, colgroup => 1, |
| 5120 | tbody => 1, tfoot => 1, thead => 1, | tbody => 1, tfoot => 1, thead => 1, |
| # | Line 4370 sub _tree_construction_main ($) { | Line 5126 sub _tree_construction_main ($) { |
| 5126 | my $i; | my $i; |
| 5127 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5128 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5129 | if ($node->[1] eq 'tr') { | if ($node->[1] & TABLE_ROW_EL) { |
| 5130 | !!!cp ('t208'); | !!!cp ('t208'); |
| 5131 | $i = $_; | $i = $_; |
| 5132 | last INSCOPE; | last INSCOPE; |
| 5133 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| html => 1, | ||
| ## NOTE: This element does not appear here, maybe. | ||
| table => 1, | ||
| }->{$node->[1]}) { | ||
| 5134 | !!!cp ('t209'); | !!!cp ('t209'); |
| 5135 | last INSCOPE; | last INSCOPE; |
| 5136 | } | } |
| 5137 | } # INSCOPE | } # INSCOPE |
| 5138 | unless (defined $i) { | unless (defined $i) { |
| 5139 | !!!cp ('t210'); | !!!cp ('t210'); |
| 5140 | ## TODO: This type is wrong. | ## TODO: This type is wrong. |
| 5141 | !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token); |
| 5142 | ## Ignore the token | ## Ignore the token |
| 5143 | !!!nack ('t210.1'); | |
| 5144 | !!!next-token; | !!!next-token; |
| 5145 | redo B; | next B; |
| 5146 | } | } |
| 5147 | ||
| 5148 | ## Clear back to table row context | ## Clear back to table row context |
| 5149 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5150 | tr => 1, html => 1, | & TABLE_ROW_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5151 | !!!cp ('t211'); | !!!cp ('t211'); |
| 5152 | ## ISSUE: Can this case be reached? | ## ISSUE: Can this case be reached? |
| 5153 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 4407 sub _tree_construction_main ($) { | Line 5158 sub _tree_construction_main ($) { |
| 5158 | if ($token->{tag_name} eq 'tr') { | if ($token->{tag_name} eq 'tr') { |
| 5159 | !!!cp ('t212'); | !!!cp ('t212'); |
| 5160 | ## reprocess | ## reprocess |
| 5161 | redo B; | !!!ack-later; |
| 5162 | next B; | |
| 5163 | } else { | } else { |
| 5164 | !!!cp ('t213'); | !!!cp ('t213'); |
| 5165 | ## reprocess in the "in table body" insertion mode... | ## reprocess in the "in table body" insertion mode... |
| # | Line 4419 sub _tree_construction_main ($) { | Line 5171 sub _tree_construction_main ($) { |
| 5171 | my $i; | my $i; |
| 5172 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5173 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5174 | if ({ | if ($node->[1] & TABLE_ROW_GROUP_EL) { |
| tbody => 1, thead => 1, tfoot => 1, | ||
| }->{$node->[1]}) { | ||
| 5175 | !!!cp ('t214'); | !!!cp ('t214'); |
| 5176 | $i = $_; | $i = $_; |
| 5177 | last INSCOPE; | last INSCOPE; |
| 5178 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5179 | !!!cp ('t215'); | !!!cp ('t215'); |
| 5180 | last INSCOPE; | last INSCOPE; |
| 5181 | } | } |
| # | Line 4435 sub _tree_construction_main ($) { | Line 5183 sub _tree_construction_main ($) { |
| 5183 | unless (defined $i) { | unless (defined $i) { |
| 5184 | !!!cp ('t216'); | !!!cp ('t216'); |
| 5185 | ## TODO: This erorr type ios wrong. | ## TODO: This erorr type ios wrong. |
| 5186 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5187 | ## Ignore the token | ## Ignore the token |
| 5188 | !!!nack ('t216.1'); | |
| 5189 | !!!next-token; | !!!next-token; |
| 5190 | redo B; | next B; |
| 5191 | } | } |
| 5192 | ||
| 5193 | ## Clear back to table body context | ## Clear back to table body context |
| 5194 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5195 | tbody => 1, tfoot => 1, thead => 1, html => 1, | & TABLE_ROWS_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5196 | !!!cp ('t217'); | !!!cp ('t217'); |
| 5197 | ## ISSUE: Can this state be reached? | ## ISSUE: Can this state be reached? |
| 5198 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 4466 sub _tree_construction_main ($) { | Line 5214 sub _tree_construction_main ($) { |
| 5214 | ||
| 5215 | if ($token->{tag_name} eq 'col') { | if ($token->{tag_name} eq 'col') { |
| 5216 | ## Clear back to table context | ## Clear back to table context |
| 5217 | while ($self->{open_elements}->[-1]->[1] ne 'table' and | while (not ($self->{open_elements}->[-1]->[1] |
| 5218 | $self->{open_elements}->[-1]->[1] ne 'html') { | & TABLE_SCOPING_EL)) { |
| 5219 | !!!cp ('t219'); | !!!cp ('t219'); |
| 5220 | ## ISSUE: Can this state be reached? | ## ISSUE: Can this state be reached? |
| 5221 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5222 | } | } |
| 5223 | ||
| 5224 | !!!insert-element ('colgroup'); | !!!insert-element ('colgroup',, $token); |
| 5225 | $self->{insertion_mode} = IN_COLUMN_GROUP_IM; | $self->{insertion_mode} = IN_COLUMN_GROUP_IM; |
| 5226 | ## reprocess | ## reprocess |
| 5227 | redo B; | !!!ack-later; |
| 5228 | next B; | |
| 5229 | } elsif ({ | } elsif ({ |
| 5230 | caption => 1, | caption => 1, |
| 5231 | colgroup => 1, | colgroup => 1, |
| 5232 | tbody => 1, tfoot => 1, thead => 1, | tbody => 1, tfoot => 1, thead => 1, |
| 5233 | }->{$token->{tag_name}}) { | }->{$token->{tag_name}}) { |
| 5234 | ## Clear back to table context | ## Clear back to table context |
| 5235 | while ($self->{open_elements}->[-1]->[1] ne 'table' and | while (not ($self->{open_elements}->[-1]->[1] |
| 5236 | $self->{open_elements}->[-1]->[1] ne 'html') { | & TABLE_SCOPING_EL)) { |
| 5237 | !!!cp ('t220'); | !!!cp ('t220'); |
| 5238 | ## ISSUE: Can this state be reached? | ## ISSUE: Can this state be reached? |
| 5239 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 4493 sub _tree_construction_main ($) { | Line 5242 sub _tree_construction_main ($) { |
| 5242 | push @$active_formatting_elements, ['#marker', ''] | push @$active_formatting_elements, ['#marker', ''] |
| 5243 | if $token->{tag_name} eq 'caption'; | if $token->{tag_name} eq 'caption'; |
| 5244 | ||
| 5245 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 5246 | $self->{insertion_mode} = { | $self->{insertion_mode} = { |
| 5247 | caption => IN_CAPTION_IM, | caption => IN_CAPTION_IM, |
| 5248 | colgroup => IN_COLUMN_GROUP_IM, | colgroup => IN_COLUMN_GROUP_IM, |
| # | Line 4502 sub _tree_construction_main ($) { | Line 5251 sub _tree_construction_main ($) { |
| 5251 | thead => IN_TABLE_BODY_IM, | thead => IN_TABLE_BODY_IM, |
| 5252 | }->{$token->{tag_name}}; | }->{$token->{tag_name}}; |
| 5253 | !!!next-token; | !!!next-token; |
| 5254 | redo B; | !!!nack ('t220.1'); |
| 5255 | next B; | |
| 5256 | } else { | } else { |
| 5257 | die "$0: in table: <>: $token->{tag_name}"; | die "$0: in table: <>: $token->{tag_name}"; |
| 5258 | } | } |
| 5259 | } elsif ($token->{tag_name} eq 'table') { | } elsif ($token->{tag_name} eq 'table') { |
| 5260 | !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]); | !!!parse-error (type => 'not closed', |
| 5261 | value => $self->{open_elements}->[-1]->[0] | |
| 5262 | ->manakai_local_name, | |
| 5263 | token => $token); | |
| 5264 | ||
| 5265 | ## As if </table> | ## As if </table> |
| 5266 | ## have a table element in table scope | ## have a table element in table scope |
| 5267 | my $i; | my $i; |
| 5268 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5269 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5270 | if ($node->[1] eq 'table') { | if ($node->[1] & TABLE_EL) { |
| 5271 | !!!cp ('t221'); | !!!cp ('t221'); |
| 5272 | $i = $_; | $i = $_; |
| 5273 | last INSCOPE; | last INSCOPE; |
| 5274 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| #table => 1, | ||
| html => 1, | ||
| }->{$node->[1]}) { | ||
| 5275 | !!!cp ('t222'); | !!!cp ('t222'); |
| 5276 | last INSCOPE; | last INSCOPE; |
| 5277 | } | } |
| # | Line 4529 sub _tree_construction_main ($) { | Line 5279 sub _tree_construction_main ($) { |
| 5279 | unless (defined $i) { | unless (defined $i) { |
| 5280 | !!!cp ('t223'); | !!!cp ('t223'); |
| 5281 | ## TODO: The following is wrong, maybe. | ## TODO: The following is wrong, maybe. |
| 5282 | !!!parse-error (type => 'unmatched end tag:table'); | !!!parse-error (type => 'unmatched end tag:table', token => $token); |
| 5283 | ## Ignore tokens </table><table> | ## Ignore tokens </table><table> |
| 5284 | !!!nack ('t223.1'); | |
| 5285 | !!!next-token; | !!!next-token; |
| 5286 | redo B; | next B; |
| 5287 | } | } |
| 5288 | ||
| 5289 | ## TODO: Followings are removed from the latest spec. | ## TODO: Followings are removed from the latest spec. |
| 5290 | ## generate implied end tags | ## generate implied end tags |
| 5291 | while ({ | while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) { |
| dd => 1, dt => 1, li => 1, p => 1, | ||
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5292 | !!!cp ('t224'); | !!!cp ('t224'); |
| 5293 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5294 | } | } |
| 5295 | ||
| 5296 | if ($self->{open_elements}->[-1]->[1] ne 'table') { | unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) { |
| 5297 | !!!cp ('t225'); | !!!cp ('t225'); |
| 5298 | ## ISSUE: Can this case be reached? | ## NOTE: |<table><tr><table>| |
| 5299 | !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]); | !!!parse-error (type => 'not closed', |
| 5300 | value => $self->{open_elements}->[-1]->[0] | |
| 5301 | ->manakai_local_name, | |
| 5302 | token => $token); | |
| 5303 | } else { | } else { |
| 5304 | !!!cp ('t226'); | !!!cp ('t226'); |
| 5305 | } | } |
| # | Line 4557 sub _tree_construction_main ($) { | Line 5309 sub _tree_construction_main ($) { |
| 5309 | ||
| 5310 | $self->_reset_insertion_mode; | $self->_reset_insertion_mode; |
| 5311 | ||
| 5312 | ## reprocess | ## reprocess |
| 5313 | redo B; | !!!ack-later; |
| 5314 | next B; | |
| 5315 | } elsif ($token->{tag_name} eq 'style') { | } elsif ($token->{tag_name} eq 'style') { |
| 5316 | if (not $open_tables->[-1]->[1]) { # tainted | if (not $open_tables->[-1]->[1]) { # tainted |
| 5317 | !!!cp ('t227.8'); | !!!cp ('t227.8'); |
| 5318 | ## NOTE: This is a "as if in head" code clone. | ## NOTE: This is a "as if in head" code clone. |
| 5319 | $parse_rcdata->(CDATA_CONTENT_MODEL); | $parse_rcdata->(CDATA_CONTENT_MODEL); |
| 5320 | redo B; | next B; |
| 5321 | } else { | } else { |
| 5322 | !!!cp ('t227.7'); | !!!cp ('t227.7'); |
| 5323 | # | # |
| # | Line 4574 sub _tree_construction_main ($) { | Line 5327 sub _tree_construction_main ($) { |
| 5327 | !!!cp ('t227.6'); | !!!cp ('t227.6'); |
| 5328 | ## NOTE: This is a "as if in head" code clone. | ## NOTE: This is a "as if in head" code clone. |
| 5329 | $script_start_tag->(); | $script_start_tag->(); |
| 5330 | redo B; | next B; |
| 5331 | } else { | } else { |
| 5332 | !!!cp ('t227.5'); | !!!cp ('t227.5'); |
| 5333 | # | # |
| # | Line 4585 sub _tree_construction_main ($) { | Line 5338 sub _tree_construction_main ($) { |
| 5338 | my $type = lc $token->{attributes}->{type}->{value}; | my $type = lc $token->{attributes}->{type}->{value}; |
| 5339 | if ($type eq 'hidden') { | if ($type eq 'hidden') { |
| 5340 | !!!cp ('t227.3'); | !!!cp ('t227.3'); |
| 5341 | !!!parse-error (type => 'in table:'.$token->{tag_name}); | !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token); |
| 5342 | ||
| 5343 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 5344 | ||
| 5345 | ## TODO: form element pointer | ## TODO: form element pointer |
| 5346 | ||
| 5347 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5348 | ||
| 5349 | !!!next-token; | !!!next-token; |
| 5350 | redo B; | !!!ack ('t227.2.1'); |
| 5351 | next B; | |
| 5352 | } else { | } else { |
| 5353 | !!!cp ('t227.2'); | !!!cp ('t227.2'); |
| 5354 | # | # |
| # | Line 4612 sub _tree_construction_main ($) { | Line 5366 sub _tree_construction_main ($) { |
| 5366 | # | # |
| 5367 | } | } |
| 5368 | ||
| 5369 | !!!parse-error (type => 'in table:'.$token->{tag_name}); | !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token); |
| 5370 | ||
| 5371 | $insert = $insert_to_foster; | $insert = $insert_to_foster; |
| 5372 | # | # |
| # | Line 4623 sub _tree_construction_main ($) { | Line 5377 sub _tree_construction_main ($) { |
| 5377 | my $i; | my $i; |
| 5378 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5379 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5380 | if ($node->[1] eq $token->{tag_name}) { | if ($node->[1] & TABLE_ROW_EL) { |
| 5381 | !!!cp ('t228'); | !!!cp ('t228'); |
| 5382 | $i = $_; | $i = $_; |
| 5383 | last INSCOPE; | last INSCOPE; |
| 5384 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5385 | !!!cp ('t229'); | !!!cp ('t229'); |
| 5386 | last INSCOPE; | last INSCOPE; |
| 5387 | } | } |
| 5388 | } # INSCOPE | } # INSCOPE |
| 5389 | unless (defined $i) { | unless (defined $i) { |
| 5390 | !!!cp ('t230'); | !!!cp ('t230'); |
| 5391 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5392 | ## Ignore the token | ## Ignore the token |
| 5393 | !!!nack ('t230.1'); | |
| 5394 | !!!next-token; | !!!next-token; |
| 5395 | redo B; | next B; |
| 5396 | } else { | } else { |
| 5397 | !!!cp ('t232'); | !!!cp ('t232'); |
| 5398 | } | } |
| 5399 | ||
| 5400 | ## Clear back to table row context | ## Clear back to table row context |
| 5401 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5402 | tr => 1, html => 1, | & TABLE_ROW_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5403 | !!!cp ('t231'); | !!!cp ('t231'); |
| 5404 | ## ISSUE: Can this state be reached? | ## ISSUE: Can this state be reached? |
| 5405 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 4656 sub _tree_construction_main ($) { | Line 5408 sub _tree_construction_main ($) { |
| 5408 | pop @{$self->{open_elements}}; # tr | pop @{$self->{open_elements}}; # tr |
| 5409 | $self->{insertion_mode} = IN_TABLE_BODY_IM; | $self->{insertion_mode} = IN_TABLE_BODY_IM; |
| 5410 | !!!next-token; | !!!next-token; |
| 5411 | redo B; | !!!nack ('t231.1'); |
| 5412 | next B; | |
| 5413 | } elsif ($token->{tag_name} eq 'table') { | } elsif ($token->{tag_name} eq 'table') { |
| 5414 | if ($self->{insertion_mode} == IN_ROW_IM) { | if ($self->{insertion_mode} == IN_ROW_IM) { |
| 5415 | ## As if </tr> | ## As if </tr> |
| # | Line 4664 sub _tree_construction_main ($) { | Line 5417 sub _tree_construction_main ($) { |
| 5417 | my $i; | my $i; |
| 5418 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5419 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5420 | if ($node->[1] eq 'tr') { | if ($node->[1] & TABLE_ROW_EL) { |
| 5421 | !!!cp ('t233'); | !!!cp ('t233'); |
| 5422 | $i = $_; | $i = $_; |
| 5423 | last INSCOPE; | last INSCOPE; |
| 5424 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5425 | !!!cp ('t234'); | !!!cp ('t234'); |
| 5426 | last INSCOPE; | last INSCOPE; |
| 5427 | } | } |
| # | Line 4678 sub _tree_construction_main ($) { | Line 5429 sub _tree_construction_main ($) { |
| 5429 | unless (defined $i) { | unless (defined $i) { |
| 5430 | !!!cp ('t235'); | !!!cp ('t235'); |
| 5431 | ## TODO: The following is wrong. | ## TODO: The following is wrong. |
| 5432 | !!!parse-error (type => 'unmatched end tag:'.$token->{type}); | !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token); |
| 5433 | ## Ignore the token | ## Ignore the token |
| 5434 | !!!nack ('t236.1'); | |
| 5435 | !!!next-token; | !!!next-token; |
| 5436 | redo B; | next B; |
| 5437 | } | } |
| 5438 | ||
| 5439 | ## Clear back to table row context | ## Clear back to table row context |
| 5440 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5441 | tr => 1, html => 1, | & TABLE_ROW_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5442 | !!!cp ('t236'); | !!!cp ('t236'); |
| 5443 | ## ISSUE: Can this state be reached? | ## ISSUE: Can this state be reached? |
| 5444 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 4703 sub _tree_construction_main ($) { | Line 5454 sub _tree_construction_main ($) { |
| 5454 | my $i; | my $i; |
| 5455 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5456 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5457 | if ({ | if ($node->[1] & TABLE_ROW_GROUP_EL) { |
| tbody => 1, thead => 1, tfoot => 1, | ||
| }->{$node->[1]}) { | ||
| 5458 | !!!cp ('t237'); | !!!cp ('t237'); |
| 5459 | $i = $_; | $i = $_; |
| 5460 | last INSCOPE; | last INSCOPE; |
| 5461 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5462 | !!!cp ('t238'); | !!!cp ('t238'); |
| 5463 | last INSCOPE; | last INSCOPE; |
| 5464 | } | } |
| 5465 | } # INSCOPE | } # INSCOPE |
| 5466 | unless (defined $i) { | unless (defined $i) { |
| 5467 | !!!cp ('t239'); | !!!cp ('t239'); |
| 5468 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5469 | ## Ignore the token | ## Ignore the token |
| 5470 | !!!nack ('t239.1'); | |
| 5471 | !!!next-token; | !!!next-token; |
| 5472 | redo B; | next B; |
| 5473 | } | } |
| 5474 | ||
| 5475 | ## Clear back to table body context | ## Clear back to table body context |
| 5476 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5477 | tbody => 1, tfoot => 1, thead => 1, html => 1, | & TABLE_ROWS_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5478 | !!!cp ('t240'); | !!!cp ('t240'); |
| 5479 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5480 | } | } |
| # | Line 4753 sub _tree_construction_main ($) { | Line 5500 sub _tree_construction_main ($) { |
| 5500 | my $i; | my $i; |
| 5501 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5502 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5503 | if ($node->[1] eq $token->{tag_name}) { | if ($node->[1] & TABLE_EL) { |
| 5504 | !!!cp ('t241'); | !!!cp ('t241'); |
| 5505 | $i = $_; | $i = $_; |
| 5506 | last INSCOPE; | last INSCOPE; |
| 5507 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5508 | !!!cp ('t242'); | !!!cp ('t242'); |
| 5509 | last INSCOPE; | last INSCOPE; |
| 5510 | } | } |
| 5511 | } # INSCOPE | } # INSCOPE |
| 5512 | unless (defined $i) { | unless (defined $i) { |
| 5513 | !!!cp ('t243'); | !!!cp ('t243'); |
| 5514 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5515 | ## Ignore the token | ## Ignore the token |
| 5516 | !!!nack ('t243.1'); | |
| 5517 | !!!next-token; | !!!next-token; |
| 5518 | redo B; | next B; |
| 5519 | } | } |
| 5520 | ||
| 5521 | splice @{$self->{open_elements}}, $i; | splice @{$self->{open_elements}}, $i; |
| # | Line 4778 sub _tree_construction_main ($) { | Line 5524 sub _tree_construction_main ($) { |
| 5524 | $self->_reset_insertion_mode; | $self->_reset_insertion_mode; |
| 5525 | ||
| 5526 | !!!next-token; | !!!next-token; |
| 5527 | redo B; | next B; |
| 5528 | } elsif ({ | } elsif ({ |
| 5529 | tbody => 1, tfoot => 1, thead => 1, | tbody => 1, tfoot => 1, thead => 1, |
| 5530 | }->{$token->{tag_name}} and | }->{$token->{tag_name}} and |
| # | Line 4788 sub _tree_construction_main ($) { | Line 5534 sub _tree_construction_main ($) { |
| 5534 | my $i; | my $i; |
| 5535 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5536 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5537 | if ($node->[1] eq $token->{tag_name}) { | if ($node->[0]->manakai_local_name eq $token->{tag_name}) { |
| 5538 | !!!cp ('t247'); | !!!cp ('t247'); |
| 5539 | $i = $_; | $i = $_; |
| 5540 | last INSCOPE; | last INSCOPE; |
| 5541 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5542 | !!!cp ('t248'); | !!!cp ('t248'); |
| 5543 | last INSCOPE; | last INSCOPE; |
| 5544 | } | } |
| 5545 | } # INSCOPE | } # INSCOPE |
| 5546 | unless (defined $i) { | unless (defined $i) { |
| 5547 | !!!cp ('t249'); | !!!cp ('t249'); |
| 5548 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5549 | ## Ignore the token | ## Ignore the token |
| 5550 | !!!nack ('t249.1'); | |
| 5551 | !!!next-token; | !!!next-token; |
| 5552 | redo B; | next B; |
| 5553 | } | } |
| 5554 | ||
| 5555 | ## As if </tr> | ## As if </tr> |
| # | Line 4812 sub _tree_construction_main ($) { | Line 5557 sub _tree_construction_main ($) { |
| 5557 | my $i; | my $i; |
| 5558 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5559 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5560 | if ($node->[1] eq 'tr') { | if ($node->[1] & TABLE_ROW_EL) { |
| 5561 | !!!cp ('t250'); | !!!cp ('t250'); |
| 5562 | $i = $_; | $i = $_; |
| 5563 | last INSCOPE; | last INSCOPE; |
| 5564 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5565 | !!!cp ('t251'); | !!!cp ('t251'); |
| 5566 | last INSCOPE; | last INSCOPE; |
| 5567 | } | } |
| 5568 | } # INSCOPE | } # INSCOPE |
| 5569 | unless (defined $i) { | unless (defined $i) { |
| 5570 | !!!cp ('t252'); | !!!cp ('t252'); |
| 5571 | !!!parse-error (type => 'unmatched end tag:tr'); | !!!parse-error (type => 'unmatched end tag:tr', token => $token); |
| 5572 | ## Ignore the token | ## Ignore the token |
| 5573 | !!!nack ('t252.1'); | |
| 5574 | !!!next-token; | !!!next-token; |
| 5575 | redo B; | next B; |
| 5576 | } | } |
| 5577 | ||
| 5578 | ## Clear back to table row context | ## Clear back to table row context |
| 5579 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5580 | tr => 1, html => 1, | & TABLE_ROW_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5581 | !!!cp ('t253'); | !!!cp ('t253'); |
| 5582 | ## ISSUE: Can this case be reached? | ## ISSUE: Can this case be reached? |
| 5583 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 4849 sub _tree_construction_main ($) { | Line 5592 sub _tree_construction_main ($) { |
| 5592 | my $i; | my $i; |
| 5593 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5594 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5595 | if ($node->[1] eq $token->{tag_name}) { | if ($node->[0]->manakai_local_name eq $token->{tag_name}) { |
| 5596 | !!!cp ('t254'); | !!!cp ('t254'); |
| 5597 | $i = $_; | $i = $_; |
| 5598 | last INSCOPE; | last INSCOPE; |
| 5599 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5600 | !!!cp ('t255'); | !!!cp ('t255'); |
| 5601 | last INSCOPE; | last INSCOPE; |
| 5602 | } | } |
| 5603 | } # INSCOPE | } # INSCOPE |
| 5604 | unless (defined $i) { | unless (defined $i) { |
| 5605 | !!!cp ('t256'); | !!!cp ('t256'); |
| 5606 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5607 | ## Ignore the token | ## Ignore the token |
| 5608 | !!!nack ('t256.1'); | |
| 5609 | !!!next-token; | !!!next-token; |
| 5610 | redo B; | next B; |
| 5611 | } | } |
| 5612 | ||
| 5613 | ## Clear back to table body context | ## Clear back to table body context |
| 5614 | while (not { | while (not ($self->{open_elements}->[-1]->[1] |
| 5615 | tbody => 1, tfoot => 1, thead => 1, html => 1, | & TABLE_ROWS_SCOPING_EL)) { |
| }->{$self->{open_elements}->[-1]->[1]}) { | ||
| 5616 | !!!cp ('t257'); | !!!cp ('t257'); |
| 5617 | ## ISSUE: Can this case be reached? | ## ISSUE: Can this case be reached? |
| 5618 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| # | Line 4879 sub _tree_construction_main ($) { | Line 5620 sub _tree_construction_main ($) { |
| 5620 | ||
| 5621 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5622 | $self->{insertion_mode} = IN_TABLE_IM; | $self->{insertion_mode} = IN_TABLE_IM; |
| 5623 | !!!nack ('t257.1'); | |
| 5624 | !!!next-token; | !!!next-token; |
| 5625 | redo B; | next B; |
| 5626 | } elsif ({ | } elsif ({ |
| 5627 | body => 1, caption => 1, col => 1, colgroup => 1, | body => 1, caption => 1, col => 1, colgroup => 1, |
| 5628 | html => 1, td => 1, th => 1, | html => 1, td => 1, th => 1, |
| 5629 | tr => 1, # $self->{insertion_mode} == IN_ROW_IM | tr => 1, # $self->{insertion_mode} == IN_ROW_IM |
| 5630 | tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM | tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM |
| 5631 | }->{$token->{tag_name}}) { | }->{$token->{tag_name}}) { |
| 5632 | !!!cp ('t258'); | !!!cp ('t258'); |
| 5633 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5634 | ## Ignore the token | ## Ignore the token |
| 5635 | !!!next-token; | !!!nack ('t258.1'); |
| 5636 | redo B; | !!!next-token; |
| 5637 | next B; | |
| 5638 | } else { | } else { |
| 5639 | !!!cp ('t259'); | !!!cp ('t259'); |
| 5640 | !!!parse-error (type => 'in table:/'.$token->{tag_name}); | !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token); |
| 5641 | ||
| 5642 | $insert = $insert_to_foster; | $insert = $insert_to_foster; |
| 5643 | # | # |
| 5644 | } | } |
| 5645 | } elsif ($token->{type} == END_OF_FILE_TOKEN) { | } elsif ($token->{type} == END_OF_FILE_TOKEN) { |
| 5646 | unless ($self->{open_elements}->[-1]->[1] eq 'html' and | unless ($self->{open_elements}->[-1]->[1] & HTML_EL and |
| 5647 | @{$self->{open_elements}} == 1) { # redundant, maybe | @{$self->{open_elements}} == 1) { # redundant, maybe |
| 5648 | !!!parse-error (type => 'in body:#eof'); | !!!parse-error (type => 'in body:#eof', token => $token); |
| 5649 | !!!cp ('t259.1'); | !!!cp ('t259.1'); |
| 5650 | # | # |
| 5651 | } else { | } else { |
| # | Line 4922 sub _tree_construction_main ($) { | Line 5665 sub _tree_construction_main ($) { |
| 5665 | unless (length $token->{data}) { | unless (length $token->{data}) { |
| 5666 | !!!cp ('t260'); | !!!cp ('t260'); |
| 5667 | !!!next-token; | !!!next-token; |
| 5668 | redo B; | next B; |
| 5669 | } | } |
| 5670 | } | } |
| 5671 | ||
| # | Line 4931 sub _tree_construction_main ($) { | Line 5674 sub _tree_construction_main ($) { |
| 5674 | } elsif ($token->{type} == START_TAG_TOKEN) { | } elsif ($token->{type} == START_TAG_TOKEN) { |
| 5675 | if ($token->{tag_name} eq 'col') { | if ($token->{tag_name} eq 'col') { |
| 5676 | !!!cp ('t262'); | !!!cp ('t262'); |
| 5677 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 5678 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5679 | !!!ack ('t262.1'); | |
| 5680 | !!!next-token; | !!!next-token; |
| 5681 | redo B; | next B; |
| 5682 | } else { | } else { |
| 5683 | !!!cp ('t263'); | !!!cp ('t263'); |
| 5684 | # | # |
| 5685 | } | } |
| 5686 | } elsif ($token->{type} == END_TAG_TOKEN) { | } elsif ($token->{type} == END_TAG_TOKEN) { |
| 5687 | if ($token->{tag_name} eq 'colgroup') { | if ($token->{tag_name} eq 'colgroup') { |
| 5688 | if ($self->{open_elements}->[-1]->[1] eq 'html') { | if ($self->{open_elements}->[-1]->[1] & HTML_EL) { |
| 5689 | !!!cp ('t264'); | !!!cp ('t264'); |
| 5690 | !!!parse-error (type => 'unmatched end tag:colgroup'); | !!!parse-error (type => 'unmatched end tag:colgroup', token => $token); |
| 5691 | ## Ignore the token | ## Ignore the token |
| 5692 | !!!next-token; | !!!next-token; |
| 5693 | redo B; | next B; |
| 5694 | } else { | } else { |
| 5695 | !!!cp ('t265'); | !!!cp ('t265'); |
| 5696 | pop @{$self->{open_elements}}; # colgroup | pop @{$self->{open_elements}}; # colgroup |
| 5697 | $self->{insertion_mode} = IN_TABLE_IM; | $self->{insertion_mode} = IN_TABLE_IM; |
| 5698 | !!!next-token; | !!!next-token; |
| 5699 | redo B; | next B; |
| 5700 | } | } |
| 5701 | } elsif ($token->{tag_name} eq 'col') { | } elsif ($token->{tag_name} eq 'col') { |
| 5702 | !!!cp ('t266'); | !!!cp ('t266'); |
| 5703 | !!!parse-error (type => 'unmatched end tag:col'); | !!!parse-error (type => 'unmatched end tag:col', token => $token); |
| 5704 | ## Ignore the token | ## Ignore the token |
| 5705 | !!!next-token; | !!!next-token; |
| 5706 | redo B; | next B; |
| 5707 | } else { | } else { |
| 5708 | !!!cp ('t267'); | !!!cp ('t267'); |
| 5709 | # | # |
| 5710 | } | } |
| 5711 | } elsif ($token->{type} == END_OF_FILE_TOKEN) { | } elsif ($token->{type} == END_OF_FILE_TOKEN) { |
| 5712 | if ($self->{open_elements}->[-1]->[1] eq 'html' or | if ($self->{open_elements}->[-1]->[1] & HTML_EL and |
| 5713 | @{$self->{open_elements}} == 1) { # redundant, maybe | @{$self->{open_elements}} == 1) { # redundant, maybe |
| 5714 | !!!cp ('t270.2'); | !!!cp ('t270.2'); |
| 5715 | ## Stop parsing. | ## Stop parsing. |
| # | Line 4976 sub _tree_construction_main ($) { | Line 5720 sub _tree_construction_main ($) { |
| 5720 | pop @{$self->{open_elements}}; # colgroup | pop @{$self->{open_elements}}; # colgroup |
| 5721 | $self->{insertion_mode} = IN_TABLE_IM; | $self->{insertion_mode} = IN_TABLE_IM; |
| 5722 | ## Reprocess. | ## Reprocess. |
| 5723 | redo B; | next B; |
| 5724 | } | } |
| 5725 | } else { | } else { |
| 5726 | die "$0: $token->{type}: Unknown token type"; | die "$0: $token->{type}: Unknown token type"; |
| 5727 | } | } |
| 5728 | ||
| 5729 | ## As if </colgroup> | ## As if </colgroup> |
| 5730 | if ($self->{open_elements}->[-1]->[1] eq 'html') { | if ($self->{open_elements}->[-1]->[1] & HTML_EL) { |
| 5731 | !!!cp ('t269'); | !!!cp ('t269'); |
| 5732 | ## TODO: Wrong error type? | ## TODO: Wrong error type? |
| 5733 | !!!parse-error (type => 'unmatched end tag:colgroup'); | !!!parse-error (type => 'unmatched end tag:colgroup', token => $token); |
| 5734 | ## Ignore the token | ## Ignore the token |
| 5735 | !!!nack ('t269.1'); | |
| 5736 | !!!next-token; | !!!next-token; |
| 5737 | redo B; | next B; |
| 5738 | } else { | } else { |
| 5739 | !!!cp ('t270'); | !!!cp ('t270'); |
| 5740 | pop @{$self->{open_elements}}; # colgroup | pop @{$self->{open_elements}}; # colgroup |
| 5741 | $self->{insertion_mode} = IN_TABLE_IM; | $self->{insertion_mode} = IN_TABLE_IM; |
| 5742 | !!!ack-later; | |
| 5743 | ## reprocess | ## reprocess |
| 5744 | redo B; | next B; |
| 5745 | } | } |
| 5746 | } elsif ($self->{insertion_mode} & SELECT_IMS) { | } elsif ($self->{insertion_mode} & SELECT_IMS) { |
| 5747 | if ($token->{type} == CHARACTER_TOKEN) { | if ($token->{type} == CHARACTER_TOKEN) { |
| 5748 | !!!cp ('t271'); | !!!cp ('t271'); |
| 5749 | $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data}); | $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data}); |
| 5750 | !!!next-token; | !!!next-token; |
| 5751 | redo B; | next B; |
| 5752 | } elsif ($token->{type} == START_TAG_TOKEN) { | } elsif ($token->{type} == START_TAG_TOKEN) { |
| 5753 | if ($token->{tag_name} eq 'option') { | if ($token->{tag_name} eq 'option') { |
| 5754 | if ($self->{open_elements}->[-1]->[1] eq 'option') { | if ($self->{open_elements}->[-1]->[1] & OPTION_EL) { |
| 5755 | !!!cp ('t272'); | !!!cp ('t272'); |
| 5756 | ## As if </option> | ## As if </option> |
| 5757 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5758 | } else { | } else { |
| 5759 | !!!cp ('t273'); | !!!cp ('t273'); |
| 5760 | } | } |
| 5761 | ||
| 5762 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 5763 | !!!next-token; | !!!nack ('t273.1'); |
| 5764 | redo B; | !!!next-token; |
| 5765 | } elsif ($token->{tag_name} eq 'optgroup') { | next B; |
| 5766 | if ($self->{open_elements}->[-1]->[1] eq 'option') { | } elsif ($token->{tag_name} eq 'optgroup') { |
| 5767 | !!!cp ('t274'); | if ($self->{open_elements}->[-1]->[1] & OPTION_EL) { |
| 5768 | ## As if </option> | !!!cp ('t274'); |
| 5769 | pop @{$self->{open_elements}}; | ## As if </option> |
| 5770 | } else { | pop @{$self->{open_elements}}; |
| 5771 | !!!cp ('t275'); | } else { |
| 5772 | } | !!!cp ('t275'); |
| 5773 | } | |
| 5774 | ||
| 5775 | if ($self->{open_elements}->[-1]->[1] eq 'optgroup') { | if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) { |
| 5776 | !!!cp ('t276'); | !!!cp ('t276'); |
| 5777 | ## As if </optgroup> | ## As if </optgroup> |
| 5778 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5779 | } else { | } else { |
| 5780 | !!!cp ('t277'); | !!!cp ('t277'); |
| 5781 | } | } |
| 5782 | ||
| 5783 | !!!insert-element ($token->{tag_name}, $token->{attributes}); | !!!insert-element ($token->{tag_name}, $token->{attributes}, $token); |
| 5784 | !!!next-token; | !!!nack ('t277.1'); |
| 5785 | redo B; | !!!next-token; |
| 5786 | next B; | |
| 5787 | } elsif ($token->{tag_name} eq 'select' or | } elsif ($token->{tag_name} eq 'select' or |
| 5788 | $token->{tag_name} eq 'input' or | $token->{tag_name} eq 'input' or |
| 5789 | ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and | ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and |
| # | Line 5045 sub _tree_construction_main ($) { | Line 5793 sub _tree_construction_main ($) { |
| 5793 | tr => 1, td => 1, th => 1, | tr => 1, td => 1, th => 1, |
| 5794 | }->{$token->{tag_name}})) { | }->{$token->{tag_name}})) { |
| 5795 | ## TODO: The type below is not good - <select> is replaced by </select> | ## TODO: The type below is not good - <select> is replaced by </select> |
| 5796 | !!!parse-error (type => 'not closed:select'); | !!!parse-error (type => 'not closed:select', token => $token); |
| 5797 | ## NOTE: As if the token were </select> (<select> case) or | ## NOTE: As if the token were </select> (<select> case) or |
| 5798 | ## as if there were </select> (otherwise). | ## as if there were </select> (otherwise). |
| 5799 | ## have an element in table scope | ## have an element in table scope |
| 5800 | my $i; | my $i; |
| 5801 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5802 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5803 | if ($node->[1] eq 'select') { | if ($node->[1] & SELECT_EL) { |
| 5804 | !!!cp ('t278'); | !!!cp ('t278'); |
| 5805 | $i = $_; | $i = $_; |
| 5806 | last INSCOPE; | last INSCOPE; |
| 5807 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| 5808 | table => 1, html => 1, | !!!cp ('t279'); |
| 5809 | }->{$node->[1]}) { | last INSCOPE; |
| 5810 | !!!cp ('t279'); | } |
| 5811 | last INSCOPE; | } # INSCOPE |
| 5812 | } | unless (defined $i) { |
| 5813 | } # INSCOPE | !!!cp ('t280'); |
| 5814 | unless (defined $i) { | !!!parse-error (type => 'unmatched end tag:select', token => $token); |
| 5815 | !!!cp ('t280'); | ## Ignore the token |
| 5816 | !!!parse-error (type => 'unmatched end tag:select'); | !!!nack ('t280.1'); |
| 5817 | ## Ignore the token | !!!next-token; |
| 5818 | !!!next-token; | next B; |
| 5819 | redo B; | } |
| } | ||
| 5820 | ||
| 5821 | !!!cp ('t281'); | !!!cp ('t281'); |
| 5822 | splice @{$self->{open_elements}}, $i; | splice @{$self->{open_elements}}, $i; |
| 5823 | ||
| 5824 | $self->_reset_insertion_mode; | $self->_reset_insertion_mode; |
| 5825 | ||
| 5826 | if ($token->{tag_name} eq 'select') { | if ($token->{tag_name} eq 'select') { |
| 5827 | !!!cp ('t281.2'); | !!!nack ('t281.2'); |
| 5828 | !!!next-token; | !!!next-token; |
| 5829 | redo B; | next B; |
| 5830 | } else { | } else { |
| 5831 | !!!cp ('t281.1'); | !!!cp ('t281.1'); |
| 5832 | !!!ack-later; | |
| 5833 | ## Reprocess the token. | ## Reprocess the token. |
| 5834 | redo B; | next B; |
| 5835 | } | } |
| 5836 | } else { | } else { |
| 5837 | !!!cp ('t282'); | !!!cp ('t282'); |
| 5838 | !!!parse-error (type => 'in select:'.$token->{tag_name}); | !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token); |
| 5839 | ## Ignore the token | ## Ignore the token |
| 5840 | !!!nack ('t282.1'); | |
| 5841 | !!!next-token; | !!!next-token; |
| 5842 | redo B; | next B; |
| 5843 | } | } |
| 5844 | } elsif ($token->{type} == END_TAG_TOKEN) { | } elsif ($token->{type} == END_TAG_TOKEN) { |
| 5845 | if ($token->{tag_name} eq 'optgroup') { | if ($token->{tag_name} eq 'optgroup') { |
| 5846 | if ($self->{open_elements}->[-1]->[1] eq 'option' and | if ($self->{open_elements}->[-1]->[1] & OPTION_EL and |
| 5847 | $self->{open_elements}->[-2]->[1] eq 'optgroup') { | $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) { |
| 5848 | !!!cp ('t283'); | !!!cp ('t283'); |
| 5849 | ## As if </option> | ## As if </option> |
| 5850 | splice @{$self->{open_elements}}, -2; | splice @{$self->{open_elements}}, -2; |
| 5851 | } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') { | } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) { |
| 5852 | !!!cp ('t284'); | !!!cp ('t284'); |
| 5853 | pop @{$self->{open_elements}}; | pop @{$self->{open_elements}}; |
| 5854 | } else { | } else { |
| 5855 | !!!cp ('t285'); | !!!cp ('t285'); |
| 5856 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5857 | ## Ignore the token | ## Ignore the token |
| 5858 | } | } |
| 5859 | !!!next-token; | !!!nack ('t285.1'); |
| 5860 | redo B; | !!!next-token; |
| 5861 | } elsif ($token->{tag_name} eq 'option') { | next B; |
| 5862 | if ($self->{open_elements}->[-1]->[1] eq 'option') { | } elsif ($token->{tag_name} eq 'option') { |
| 5863 | !!!cp ('t286'); | if ($self->{open_elements}->[-1]->[1] & OPTION_EL) { |
| 5864 | pop @{$self->{open_elements}}; | !!!cp ('t286'); |
| 5865 | } else { | pop @{$self->{open_elements}}; |
| 5866 | !!!cp ('t287'); | } else { |
| 5867 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!cp ('t287'); |
| 5868 | ## Ignore the token | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5869 | } | ## Ignore the token |
| 5870 | !!!next-token; | } |
| 5871 | redo B; | !!!nack ('t287.1'); |
| 5872 | } elsif ($token->{tag_name} eq 'select') { | !!!next-token; |
| 5873 | ## have an element in table scope | next B; |
| 5874 | my $i; | } elsif ($token->{tag_name} eq 'select') { |
| 5875 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | ## have an element in table scope |
| 5876 | my $node = $self->{open_elements}->[$_]; | my $i; |
| 5877 | if ($node->[1] eq $token->{tag_name}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5878 | !!!cp ('t288'); | my $node = $self->{open_elements}->[$_]; |
| 5879 | $i = $_; | if ($node->[1] & SELECT_EL) { |
| 5880 | last INSCOPE; | !!!cp ('t288'); |
| 5881 | } elsif ({ | $i = $_; |
| 5882 | table => 1, html => 1, | last INSCOPE; |
| 5883 | }->{$node->[1]}) { | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| 5884 | !!!cp ('t289'); | !!!cp ('t289'); |
| 5885 | last INSCOPE; | last INSCOPE; |
| 5886 | } | } |
| 5887 | } # INSCOPE | } # INSCOPE |
| 5888 | unless (defined $i) { | unless (defined $i) { |
| 5889 | !!!cp ('t290'); | !!!cp ('t290'); |
| 5890 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5891 | ## Ignore the token | ## Ignore the token |
| 5892 | !!!next-token; | !!!nack ('t290.1'); |
| 5893 | redo B; | !!!next-token; |
| 5894 | } | next B; |
| 5895 | } | |
| 5896 | ||
| 5897 | !!!cp ('t291'); | !!!cp ('t291'); |
| 5898 | splice @{$self->{open_elements}}, $i; | splice @{$self->{open_elements}}, $i; |
| 5899 | ||
| 5900 | $self->_reset_insertion_mode; | $self->_reset_insertion_mode; |
| 5901 | ||
| 5902 | !!!next-token; | !!!nack ('t291.1'); |
| 5903 | redo B; | !!!next-token; |
| 5904 | next B; | |
| 5905 | } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and | } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and |
| 5906 | { | { |
| 5907 | caption => 1, table => 1, tbody => 1, | caption => 1, table => 1, tbody => 1, |
| 5908 | tfoot => 1, thead => 1, tr => 1, td => 1, th => 1, | tfoot => 1, thead => 1, tr => 1, td => 1, th => 1, |
| 5909 | }->{$token->{tag_name}}) { | }->{$token->{tag_name}}) { |
| 5910 | ## TODO: The following is wrong? | ## TODO: The following is wrong? |
| 5911 | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}); | !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token); |
| 5912 | ||
| 5913 | ## have an element in table scope | ## have an element in table scope |
| 5914 | my $i; | my $i; |
| 5915 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5916 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5917 | if ($node->[1] eq $token->{tag_name}) { | if ($node->[0]->manakai_local_name eq $token->{tag_name}) { |
| 5918 | !!!cp ('t292'); | !!!cp ('t292'); |
| 5919 | $i = $_; | $i = $_; |
| 5920 | last INSCOPE; | last INSCOPE; |
| 5921 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| 5922 | table => 1, html => 1, | !!!cp ('t293'); |
| 5923 | }->{$node->[1]}) { | last INSCOPE; |
| 5924 | !!!cp ('t293'); | } |
| 5925 | last INSCOPE; | } # INSCOPE |
| 5926 | } | unless (defined $i) { |
| 5927 | } # INSCOPE | !!!cp ('t294'); |
| 5928 | unless (defined $i) { | ## Ignore the token |
| 5929 | !!!cp ('t294'); | !!!nack ('t294.1'); |
| 5930 | ## Ignore the token | !!!next-token; |
| 5931 | !!!next-token; | next B; |
| 5932 | redo B; | } |
| } | ||
| 5933 | ||
| 5934 | ## As if </select> | ## As if </select> |
| 5935 | ## have an element in table scope | ## have an element in table scope |
| 5936 | undef $i; | undef $i; |
| 5937 | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { | INSCOPE: for (reverse 0..$#{$self->{open_elements}}) { |
| 5938 | my $node = $self->{open_elements}->[$_]; | my $node = $self->{open_elements}->[$_]; |
| 5939 | if ($node->[1] eq 'select') { | if ($node->[1] & SELECT_EL) { |
| 5940 | !!!cp ('t295'); | !!!cp ('t295'); |
| 5941 | $i = $_; | $i = $_; |
| 5942 | last INSCOPE; | last INSCOPE; |
| 5943 | } elsif ({ | } elsif ($node->[1] & TABLE_SCOPING_EL) { |
| table => 1, html => 1, | ||
| }->{$node->[1]}) { | ||
| 5944 | ## ISSUE: Can this state be reached? | ## ISSUE: Can this state be reached? |
| 5945 | !!!cp ('t296'); | !!!cp ('t296'); |
| 5946 | last INSCOPE; | last INSCOPE; |
| 5947 | } | } |
| 5948 | } # INSCOPE | } # INSCOPE |
| 5949 | unless (defined $i) { | unless (defined $i) { |
| 5950 | !!!cp ('t297'); | !!!cp ('t297'); |
| 5951 | ## TODO: The following error type is correct? | ## TODO: The following error type is correct? |
| 5952 | !!!parse-error (type => 'unmatched end tag:select'); | !!!parse-error (type => 'unmatched end tag:select', token => $token); |
| 5953 | ## Ignore the </select> token | ## Ignore the </select> token |
| 5954 | !!!next-token; ## TODO: ok? | !!!nack ('t297.1'); |
| 5955 | redo B; | !!!next-token; ## TODO: ok? |
| 5956 | } | next B; |
| 5957 | } | |
| 5958 | ||
| 5959 | !!!cp ('t298'); | !!!cp ('t298'); |
| 5960 | splice @{$self->{open_elements}}, $i; | splice @{$self->{open_elements}}, $i; |
| 5961 | ||
| 5962 | $self->_reset_insertion_mode; | $self->_reset_insertion_mode; |
| 5963 | ||
| 5964 | ## reprocess | !!!ack-later; |
| 5965 | redo B; | ## reprocess |
| 5966 | next B; | |
| 5967 | } else { | } else { |
| 5968 | !!!cp ('t299'); | !!!cp ('t299'); |
| 5969 | !!!parse-error (type => 'in select:/'.$token->{tag_name}); | !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token); |
| 5970 | ## Ignore the token | ## Ignore the token |
| 5971 | !!!nack ('t299.3'); | |
| 5972 | !!!next-token; | !!!next-token; |
| 5973 | redo B; | next B; |
| 5974 | } | } |
| 5975 | } elsif ($token->{type} == END_OF_FILE_TOKEN) { | } elsif ($token->{type} == END_OF_FILE_TOKEN) { |
| 5976 | unless ($self->{open_elements}->[-1]->[1] eq 'html' and | unless ($self->{open_elements}->[-1]->[1] & HTML_EL and |
| 5977 | @{$self->{open_elements}} == 1) { # redundant, maybe | @{$self->{open_elements}} == 1) { # redundant, maybe |
| 5978 | !!!cp ('t299.1'); | !!!cp ('t299.1'); |
| 5979 | !!!parse-error (type => 'in body:#eof'); | !!!parse-error (type => 'in body:#eof', token => $token); |
| 5980 | } else { | } else { |
| 5981 | !!!cp ('t299.2'); | !!!cp ('t299.2'); |
| 5982 | } | } |
| # | Line 5247 sub _tree_construction_main ($) { | Line 5998 sub _tree_construction_main ($) { |
| 5998 | unless (length $token->{data}) { | unless (length $token->{data}) { |
| 5999 | !!!cp ('t300'); | !!!cp ('t300'); |
| 6000 | !!!next-token; | !!!next-token; |
| 6001 | redo B; | next B; |
| 6002 | } | } |
| 6003 | } | } |
| 6004 | ||
| 6005 | if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) { | if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) { |
| 6006 | !!!cp ('t301'); | !!!cp ('t301'); |
| 6007 | !!!parse-error (type => 'after html:#character'); | !!!parse-error (type => 'after html:#character', token => $token); |
| 6008 | ||
| 6009 | ## Reprocess in the "after body" insertion mode. | ## Reprocess in the "after body" insertion mode. |
| 6010 | } else { | } else { |
| # | Line 5261 sub _tree_construction_main ($) { | Line 6012 sub _tree_construction_main ($) { |
| 6012 | } | } |
| 6013 | ||
| 6014 | ## "after body" insertion mode | ## "after body" insertion mode |
| 6015 | !!!parse-error (type => 'after body:#character'); | !!!parse-error (type => 'after body:#character', token => $token); |
| 6016 | ||
| 6017 | $self->{insertion_mode} = IN_BODY_IM; | $self->{insertion_mode} = IN_BODY_IM; |
| 6018 | ## reprocess | ## reprocess |
| 6019 | redo B; | next B; |
| 6020 | } elsif ($token->{type} == START_TAG_TOKEN) { | } elsif ($token->{type} == START_TAG_TOKEN) { |
| 6021 | if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) { | if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) { |
| 6022 | !!!cp ('t303'); | !!!cp ('t303'); |
| 6023 | !!!parse-error (type => 'after html:'.$token->{tag_name}); | !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token); |
| 6024 | ||
| 6025 | ## Reprocess in the "after body" insertion mode. | ## Reprocess in the "after body" insertion mode. |
| 6026 | } else { | } else { |
| # | Line 5277 sub _tree_construction_main ($) { | Line 6028 sub _tree_construction_main ($) { |
| 6028 | } | } |
| 6029 | ||
| 6030 | ## "after body" insertion mode | ## "after body" insertion mode |
| 6031 | !!!parse-error (type => 'after body:'.$token->{tag_name}); | !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token); |
| 6032 | ||
| 6033 | $self->{insertion_mode} = IN_BODY_IM; | $self->{insertion_mode} = IN_BODY_IM; |
| 6034 | !!!ack-later; | |
| 6035 | ## reprocess | ## reprocess |
| 6036 | redo B; | next B; |
| 6037 | } elsif ($token->{type} == END_TAG_TOKEN) { | } elsif ($token->{type} == END_TAG_TOKEN) { |
| 6038 | if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) { | if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) { |
| 6039 | !!!cp ('t305'); | !!!cp ('t305'); |
| 6040 | !!!parse-error (type => 'after html:/'.$token->{tag_name}); | !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token); |
| 6041 | ||
| 6042 | $self->{insertion_mode} = AFTER_BODY_IM; | $self->{insertion_mode} = AFTER_BODY_IM; |
| 6043 | ## Reprocess in the "after body" insertion mode. | ## Reprocess in the "after body" insertion mode. |
| # | Line 5297 sub _tree_construction_main ($) { | Line 6049 sub _tree_construction_main ($) { |
| 6049 | if ($token->{tag_name} eq 'html') { | if ($token->{tag_name} eq 'html') { |
| 6050 | if (defined $self->{inner_html_node}) { | if (defined $self->{inner_html_node}) { |
| 6051 | !!!cp ('t307'); | !!!cp ('t307'); |
| 6052 | !!!parse-error (type => 'unmatched end tag:html'); | !!!parse-error (type => 'unmatched end tag:html', token => $token); |
| 6053 | ## Ignore the token | ## Ignore the token |
| 6054 | !!!next-token; | !!!next-token; |
| 6055 | redo B; | next B; |
| 6056 | } else { | } else { |
| 6057 | !!!cp ('t308'); | !!!cp ('t308'); |
| 6058 | $self->{insertion_mode} = AFTER_HTML_BODY_IM; | $self->{insertion_mode} = AFTER_HTML_BODY_IM; |
| 6059 | !!!next-token; | !!!next-token; |
| 6060 | redo B; | next B; |
| 6061 | } | } |
| 6062 | } else { | } else { |
| 6063 | !!!cp ('t309'); | !!!cp ('t309'); |
| 6064 | !!!parse-error (type => 'after body:/'.$token->{tag_name}); | !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token); |
| 6065 | ||
| 6066 | $self->{insertion_mode} = IN_BODY_IM; | $self->{insertion_mode} = IN_BODY_IM; |
| 6067 | ## reprocess | ## reprocess |
| 6068 | redo B; | next B; |
| 6069 | } | } |
| 6070 | } elsif ($token->{type} == END_OF_FILE_TOKEN) { | } elsif ($token->{type} == END_OF_FILE_TOKEN) { |
| 6071 | !!!cp ('t309.2'); | !!!cp ('t309.2'); |
| # | Line 5330 sub _tree_construction_main ($) { | Line 6082 sub _tree_construction_main ($) { |
| 6082 | unless (length $token->{data}) { | unless (length $token->{data}) { |
| 6083 | !!!cp ('t310'); | !!!cp ('t310'); |
| 6084 | !!!next-token; | !!!next-token; |
| 6085 | redo B; | next B; |
| 6086 | } | } |
| 6087 | } | } |
| 6088 | ||
| 6089 | if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) { | if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) { |
| 6090 | if ($self->{insertion_mode} == IN_FRAMESET_IM) { | if ($self->{insertion_mode} == IN_FRAMESET_IM) { |
| 6091 | !!!cp ('t311'); | !!!cp ('t311'); |
| 6092 | !!!parse-error (type => 'in frameset:#character'); | !!!parse-error (type => 'in frameset:#character', token => $token); |
| 6093 | } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) { | } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) { |
| 6094 | !!!cp ('t312'); | !!!cp ('t312'); |
| 6095 | !!!parse-error (type => 'after frameset:#character'); | !!!parse-error (type => 'after frameset:#character', token => $token); |
| 6096 | } else { # "after html frameset" | } else { # "after html frameset" |
| 6097 | !!!cp ('t313'); | !!!cp ('t313'); |
| 6098 | !!!parse-error (type => 'after html:#character'); | !!!parse-error (type => 'after html:#character', token => $token); |
| 6099 | ||
| 6100 | $self->{insertion_mode} = AFTER_FRAMESET_IM; | $self->{insertion_mode} = AFTER_FRAMESET_IM; |
| 6101 | ## Reprocess in the "after frameset" insertion mode. | ## Reprocess in the "after frameset" insertion mode. |
| 6102 | !!!parse-error (type => 'after frameset:#character'); | !!!parse-error (type => 'after frameset:#character', token => $token); |
| 6103 | } | } |
| 6104 | ||
| 6105 | ## Ignore the token. | ## Ignore the token. |
| # | Line 5358 sub _tree_construction_main ($) { | Line 6110 sub _tree_construction_main ($) { |
| 6110 | !!!cp ('t315'); | !!!cp ('t315'); |
| 6111 | !!!next-token; | !!!next-token; |
| 6112 | } | } |
| 6113 | redo B; | next B; |
| 6114 | } | } |
| 6115 | ||
| 6116 | die qq[$0: Character "$token->{data}"]; | die qq[$0: Character "$token->{data}"]; |
| 6117 | } elsif ($token->{type} == START_TAG_TOKEN) { | } elsif ($token->{type} == START_TAG_TOKEN) { |
| 6118 | if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) { | if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) { |
| 6119 | !!!cp ('t316'); | !!!cp ('t316'); |
| 6120 | !!!parse-error (type => 'after html:'.$token->{tag_name}); | !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token); |
| 6121 | ||
| 6122 | $self->{insertion_mode} = AFTER_FRAMESET_IM; | $self->{insertion_mode} = AFTER_FRAMESET_IM; |
| 6123 | ## Process in the "after frameset" insertion mode. | ## Process in the "after frameset" insertion mode. |
| # | Line 5376 sub _tree_construction_main ($) { | Line 6128 sub _tree_construction_main ($) { |
| 6128 | if ($token->{tag_name} eq 'frameset' and | if ($token->{tag_name} eq 'frameset' and |
| 6129 | $self->{insertion_mode} == IN_FRAMESET_IM) { |