| 1 | ## NOTE: This module will be renamed as Element.pm. | 
| 2 |  | 
| 3 | package Message::DOM::Element; | 
| 4 | use strict; | 
| 5 | our $VERSION=do{my @r=(q$Revision: 1.25 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; | 
| 6 | push our @ISA, 'Message::DOM::Node', 'Message::IF::Element', | 
| 7 | 'Message::IF::ElementSelector', # MUST in Selectors API spec. | 
| 8 | 'Message::IF::ElementCSSInlineStyle'; | 
| 9 | require Message::DOM::Document; | 
| 10 |  | 
| 11 | sub ____new ($$$$$) { | 
| 12 | my $self = shift->SUPER::____new (shift); | 
| 13 | ($$self->{namespace_uri}, | 
| 14 | $$self->{prefix}, | 
| 15 | $$self->{local_name}) = @_; | 
| 16 | $$self->{attributes} = {}; | 
| 17 | $$self->{child_nodes} = []; | 
| 18 | return $self; | 
| 19 | } # ____new | 
| 20 |  | 
| 21 | sub AUTOLOAD { | 
| 22 | my $method_name = our $AUTOLOAD; | 
| 23 | $method_name =~ s/.*:://; | 
| 24 | return if $method_name eq 'DESTROY'; | 
| 25 |  | 
| 26 | if ({ | 
| 27 | ## Read-only attributes (trivial accessors) | 
| 28 | namespace_uri => 1, | 
| 29 | }->{$method_name}) { | 
| 30 | no strict 'refs'; | 
| 31 | eval qq{ | 
| 32 | sub $method_name (\$) { | 
| 33 | return \${\$_[0]}->{$method_name}; | 
| 34 | } | 
| 35 | }; | 
| 36 | goto &{ $AUTOLOAD }; | 
| 37 | } elsif ({ | 
| 38 | ## Read-write attributes (DOMString, trivial accessors) | 
| 39 | manakai_base_uri => 1, | 
| 40 | }->{$method_name}) { | 
| 41 | no strict 'refs'; | 
| 42 | eval qq{ | 
| 43 | sub $method_name (\$;\$) { | 
| 44 | if (\@_ > 1) { | 
| 45 | if (\${\${\$_[0]}->{owner_document}}->{strict_error_checking} and | 
| 46 | \${\$_[0]}->{manakai_read_only}) { | 
| 47 | report Message::DOM::DOMException | 
| 48 | -object => \$_[0], | 
| 49 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 50 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 51 | } | 
| 52 | if (defined \$_[1]) { | 
| 53 | \${\$_[0]}->{$method_name} = ''.\$_[1]; | 
| 54 | } else { | 
| 55 | delete \${\$_[0]}->{$method_name}; | 
| 56 | } | 
| 57 | } | 
| 58 | return \${\$_[0]}->{$method_name}; | 
| 59 | } | 
| 60 | }; | 
| 61 | goto &{ $AUTOLOAD }; | 
| 62 | } elsif (my $module_name = { | 
| 63 | query_selector => 'Message::DOM::SelectorsAPI', | 
| 64 | query_selector_all => 'Message::DOM::SelectorsAPI', | 
| 65 | }->{$method_name}) { | 
| 66 | eval qq{ require $module_name } or die $@; | 
| 67 | goto &{ $AUTOLOAD }; | 
| 68 | } else { | 
| 69 | require Carp; | 
| 70 | Carp::croak (qq<Can't locate method "$AUTOLOAD">); | 
| 71 | } | 
| 72 | } # AUTOLOAD | 
| 73 |  | 
| 74 | ## TODO: Test for create_element_ns ('', ...) | 
| 75 |  | 
| 76 | ## |Node| attributes | 
| 77 |  | 
| 78 | sub attributes ($) { | 
| 79 | require Message::DOM::NamedNodeMap; | 
| 80 | return bless \\($_[0]), 'Message::DOM::NamedNodeMap::AttrMap'; | 
| 81 | } # attributes | 
| 82 |  | 
| 83 | sub base_uri ($) { | 
| 84 | my $self = $_[0]; | 
| 85 | return $$self->{manakai_base_uri} if defined $$self->{manakai_base_uri}; | 
| 86 |  | 
| 87 | local $Error::Depth = $Error::Depth + 1; | 
| 88 | my $xb = $self->get_attribute_node_ns | 
| 89 | ('http://www.w3.org/XML/1998/namespace', 'base'); | 
| 90 | unless (defined $xb) { | 
| 91 | $xb = $self->get_attribute_node_ns (undef, 'xml:base'); | 
| 92 | } | 
| 93 |  | 
| 94 | if ($xb) { | 
| 95 | my $v = $self->owner_document->implementation->create_uri_reference | 
| 96 | ($xb->value); | 
| 97 | if (not defined $v->uri_scheme) { # Relative reference | 
| 98 | my $xbbase = $xb->base_uri; | 
| 99 | if (defined $xbbase) { | 
| 100 | return $v->get_absolute_reference ($xbbase)->uri_reference; | 
| 101 | } | 
| 102 | } | 
| 103 | return $v->uri_reference; | 
| 104 | } | 
| 105 |  | 
| 106 | my $pe = $$self->{parent_node}; | 
| 107 | while (defined $pe) { | 
| 108 | my $nt = $pe->node_type; | 
| 109 | if ($nt == 1 or $nt == 6 or $nt == 9 or $nt == 11) { | 
| 110 | ## Element, Entity, Document, or DocumentFragment | 
| 111 | return $pe->base_uri; | 
| 112 | } elsif ($nt == 5) { | 
| 113 | ## EntityReference | 
| 114 | if ($pe->manakai_external) { | 
| 115 | return $pe->manakai_entity_base_uri; | 
| 116 | } | 
| 117 | } | 
| 118 | $pe = $$pe->{parent_node}; | 
| 119 | } | 
| 120 | return $pe->base_uri if $pe; | 
| 121 | return $$self->{owner_document}->base_uri; | 
| 122 | } # base_uri | 
| 123 |  | 
| 124 | sub local_name ($) { # TODO: HTML5 case | 
| 125 | return ${$_[0]}->{local_name}; | 
| 126 | } # local_name | 
| 127 |  | 
| 128 | sub manakai_local_name ($) { | 
| 129 | return ${$_[0]}->{local_name}; | 
| 130 | } # manakai_local_name | 
| 131 |  | 
| 132 | sub namespace_uri ($); | 
| 133 |  | 
| 134 | ## The tag name of the element [DOM1, DOM2]. | 
| 135 | ## Same as |Element.tagName| [DOM3]. | 
| 136 |  | 
| 137 | *node_name = \&tag_name; | 
| 138 |  | 
| 139 | sub node_type () { 1 } # ELEMENT_NODE | 
| 140 |  | 
| 141 | sub prefix ($;$) { | 
| 142 | ## NOTE: No check for new value as Firefox doesn't do. | 
| 143 | ## See <http://suika.fam.cx/gate/2005/sw/prefix>. | 
| 144 |  | 
| 145 | ## NOTE: Same as trivial setter except "" -> undef | 
| 146 |  | 
| 147 | ## NOTE: Same as |Attr|'s |prefix|. | 
| 148 |  | 
| 149 | if (@_ > 1) { | 
| 150 | if (${$_[0]}->{manakai_read_only}) { | 
| 151 | report Message::DOM::DOMException | 
| 152 | -object => $_[0], | 
| 153 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 154 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 155 | } | 
| 156 | if (defined $_[1] and $_[1] ne '') { | 
| 157 | ${$_[0]}->{prefix} = ''.$_[1]; | 
| 158 | } else { | 
| 159 | delete ${$_[0]}->{prefix}; | 
| 160 | } | 
| 161 | } | 
| 162 | return ${$_[0]}->{prefix}; | 
| 163 | } # prefix | 
| 164 |  | 
| 165 | ## |Element| attributes | 
| 166 |  | 
| 167 | sub manakai_base_uri ($;$); | 
| 168 |  | 
| 169 | ## Defined in |HTMLElement| interface of HTML5 | 
| 170 | sub inner_html ($;$) { | 
| 171 | my $self = $_[0]; | 
| 172 |  | 
| 173 | ## TODO: Setter | 
| 174 |  | 
| 175 | if (${$$self->{owner_document}}->{manakai_is_html}) { | 
| 176 | require Whatpm::HTML::Serializer; | 
| 177 | return ${ Whatpm::HTML::Serializer->get_inner_html ($self) }; | 
| 178 | } else { | 
| 179 | ## TODO: This serializer is not currenly conformant to HTML5. | 
| 180 | require Whatpm::XMLSerializer; | 
| 181 | my $r = ''; | 
| 182 | for (@{$self->child_nodes}) { | 
| 183 | $r .= ${ Whatpm::XMLSerializer->get_outer_xml ($_) }; | 
| 184 | } | 
| 185 | return $r; | 
| 186 | } | 
| 187 | } # inner_html | 
| 188 |  | 
| 189 | sub schema_type_info ($) { | 
| 190 | require Message::DOM::TypeInfo; | 
| 191 | my $v = 0; | 
| 192 | return bless \$v, 'Message::DOM::TypeInfo'; | 
| 193 | ## NOTE: Currently manakai does not support XML Schema, so it is | 
| 194 | ## always a no-type |TypeInfo|.  It is expected that | 
| 195 | ## a future version of the implementation will return an | 
| 196 | ## element type definition node that also implement the | 
| 197 | ## |TypeInfo| interface when the schema language is XML DTD. | 
| 198 | } # schema_type_info | 
| 199 |  | 
| 200 | ## TODO: HTML5 capitalization | 
| 201 | sub tag_name ($) { | 
| 202 | my $self = shift; | 
| 203 | if (defined $$self->{prefix}) { | 
| 204 | return $$self->{prefix} . ':' . $$self->{local_name}; | 
| 205 | } else { | 
| 206 | return $$self->{local_name}; | 
| 207 | } | 
| 208 | } # tag_name | 
| 209 |  | 
| 210 | ## TODO: Documentation | 
| 211 | sub manakai_tag_name ($) { | 
| 212 | my $self = shift; | 
| 213 | if (defined $$self->{prefix}) { | 
| 214 | return $$self->{prefix} . ':' . $$self->{local_name}; | 
| 215 | } else { | 
| 216 | return $$self->{local_name}; | 
| 217 | } | 
| 218 | } # manakai_tag_name | 
| 219 |  | 
| 220 | ## The |Element| interface - methods | 
| 221 |  | 
| 222 | sub manakai_element_type_match ($$$) { | 
| 223 | my ($self, $nsuri, $ln) = @_; | 
| 224 | if (defined $nsuri) { | 
| 225 | if (defined $$self->{namespace_uri} and $nsuri eq $$self->{namespace_uri}) { | 
| 226 | return ($ln eq $$self->{local_name}); | 
| 227 | } else { | 
| 228 | return 0; | 
| 229 | } | 
| 230 | } else { | 
| 231 | if (not defined $$self->{namespace_uri}) { | 
| 232 | return ($ln eq $$self->{local_name}); | 
| 233 | } else { | 
| 234 | return 0; | 
| 235 | } | 
| 236 | } | 
| 237 | } # manakai_element_type_match | 
| 238 |  | 
| 239 | sub get_attribute ($$) { | 
| 240 | my $attr = ${$_[0]}->{attributes}; | 
| 241 | my $name = ''.$_[1]; | 
| 242 |  | 
| 243 | ## NOTE: |sort|ing is required so that every |getAttribute|, |setAttribute|, | 
| 244 | ## |hasAttribute|, |removeAttribute|, or any other namespace unaware | 
| 245 | ## methods operates on the same node even if there is | 
| 246 | ## multiple nodes with the same qualified name. | 
| 247 |  | 
| 248 | ## NOTE: Same as |get_attribute_node|, except what is returned. | 
| 249 |  | 
| 250 | for my $ns (sort {$a cmp $b} keys %$attr) { | 
| 251 | for my $ln (sort {$a cmp $b} keys %{$attr->{$ns}}) { | 
| 252 | my $node = $attr->{$ns}->{$ln}; | 
| 253 | if ($node->manakai_name eq $name) { | 
| 254 | return $node->value; | 
| 255 | } | 
| 256 | } | 
| 257 | } | 
| 258 |  | 
| 259 | return undef; | 
| 260 | } # get_attribute | 
| 261 |  | 
| 262 | sub get_attribute_node ($$) { | 
| 263 | my $attr = ${$_[0]}->{attributes}; | 
| 264 | my $name = ''.$_[1]; | 
| 265 |  | 
| 266 | ## NOTE: Same as |get_attribute|, except what is returned. | 
| 267 |  | 
| 268 | for my $ns (sort {$a cmp $b} keys %$attr) { | 
| 269 | for my $ln (sort {$a cmp $b} keys %{$attr->{$ns}}) { | 
| 270 | my $node = $attr->{$ns}->{$ln}; | 
| 271 | if ($node->manakai_name eq $name) { | 
| 272 | return $node; | 
| 273 | } | 
| 274 | } | 
| 275 | } | 
| 276 |  | 
| 277 | return undef; | 
| 278 | } # get_attribute_node | 
| 279 |  | 
| 280 | sub get_attribute_ns ($$$) { | 
| 281 | my $nsuri = defined $_[1] ? ''.$_[1] : ''; | 
| 282 | my $ln = ''.$_[2]; | 
| 283 | if (my $attr = ${$_[0]}->{attributes}->{$nsuri}->{$ln}) { | 
| 284 | return $attr->value; | 
| 285 | } else { | 
| 286 | return undef; | 
| 287 | } | 
| 288 | } # get_attribute_ns | 
| 289 |  | 
| 290 | sub get_attribute_node_ns ($$$) { | 
| 291 | return ${$_[0]}->{attributes}->{defined $_[1] ? ''.$_[1] : ''}->{''.$_[2]}; | 
| 292 | } # get_attribute_node_ns | 
| 293 |  | 
| 294 | *get_elements_by_tag_name = \&Message::DOM::Document::get_elements_by_tag_name; | 
| 295 |  | 
| 296 | *get_elements_by_tag_name_ns | 
| 297 | = \&Message::DOM::Document::get_elements_by_tag_name_ns; | 
| 298 |  | 
| 299 | sub has_attribute ($$) { | 
| 300 | my $attr = ${$_[0]}->{attributes}; | 
| 301 | my $name = ''.$_[1]; | 
| 302 |  | 
| 303 | for my $ns (keys %$attr) { | 
| 304 | for my $ln (keys %{$attr->{$ns}}) { | 
| 305 | my $node = $attr->{$ns}->{$ln}; | 
| 306 | if ($node->manakai_name eq $name) { | 
| 307 | return 1; | 
| 308 | } | 
| 309 | } | 
| 310 | } | 
| 311 |  | 
| 312 | return 0; | 
| 313 | } # has_attribute | 
| 314 |  | 
| 315 | sub has_attribute_ns ($$$) { | 
| 316 | return ${$_[0]}->{attributes}->{defined $_[1] ? ''.$_[1] : ''}->{''.$_[2]}?1:0; | 
| 317 | } # has_attribute_ns | 
| 318 |  | 
| 319 | sub remove_attribute ($$) { | 
| 320 | my $attr = ${$_[0]}->{attributes}; | 
| 321 | my $name = ''.$_[1]; | 
| 322 |  | 
| 323 | my $list; | 
| 324 | my $key; | 
| 325 | my $attr_node; | 
| 326 | ATTR: { | 
| 327 | for my $ns (keys %$attr) { | 
| 328 | $list = $attr->{$ns}; | 
| 329 | for my $ln (keys %$list) { | 
| 330 | $attr_node = $list->{$ln}; | 
| 331 | if ($attr_node->manakai_name eq $name) { | 
| 332 | $key = $ln; | 
| 333 | last ATTR; | 
| 334 | } | 
| 335 | } | 
| 336 | } | 
| 337 |  | 
| 338 | return undef; # not found | 
| 339 | } # ATTR | 
| 340 |  | 
| 341 | my $od = ${$_[0]}->{owner_document}; | 
| 342 | if ($$od->{strict_error_checking} and ${$_[0]}->{manakai_read_only}) { | 
| 343 | report Message::DOM::DOMException | 
| 344 | -object => $_[0], | 
| 345 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 346 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 347 | } | 
| 348 |  | 
| 349 | delete $list->{$key}; | 
| 350 | delete $$attr_node->{owner_element}; | 
| 351 | $$attr_node->{specified} = 1; | 
| 352 | delete ${$_[0]}->{manakai_content_attribute_list}; | 
| 353 |  | 
| 354 | ## Default attribute | 
| 355 | local $Error::Depth = $Error::Depth + 1; | 
| 356 | my $cfg = $od->dom_config; | 
| 357 | if ($cfg->get_parameter | 
| 358 | (q<http://suika.fam.cx/www/2006/dom-config/dtd-default-attribute>)) { | 
| 359 | my $doctype = $od->doctype; | 
| 360 | if ($doctype) { | 
| 361 | my $et = $doctype->get_element_type_definition_node | 
| 362 | ($_[0]->manakai_tag_name); | 
| 363 | if ($et) { | 
| 364 | my $at = $et->get_attribute_definition_node ($name); | 
| 365 | if ($at) { | 
| 366 | local $$od->{strict_error_checking} = 0; | 
| 367 | my $copy_asis = $cfg->get_parameter | 
| 368 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>); | 
| 369 | $cfg->set_parameter | 
| 370 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => 1); | 
| 371 | ADD: { | 
| 372 | my $def_attr_node; | 
| 373 | my $def_prefix = $attr_node->prefix; | 
| 374 | my $def_nsuri = ''; | 
| 375 | my $def_ln; | 
| 376 | if (defined $def_prefix) { | 
| 377 | $def_nsuri = | 
| 378 | $def_prefix eq 'xml' ? q<http://www.w3.org/XML/1998/namespace>: | 
| 379 | $def_prefix eq 'xmlns' ? q<http://www.w3.org/2000/xmlns/>: | 
| 380 | $_[0]->lookup_namespace_uri ($def_prefix); | 
| 381 | unless (defined $def_nsuri) { | 
| 382 | ## TODO: Namespace well-formedness error... | 
| 383 | } | 
| 384 | $def_ln = $attr_node->manakai_local_name; | 
| 385 | } else { | 
| 386 | $def_nsuri = $name eq 'xmlns' | 
| 387 | ? q<http://www.w3.org/2000/xmlns/> : undef; | 
| 388 | $def_ln = $name; | 
| 389 | } | 
| 390 | if ($attr->{defined $def_nsuri ? $def_nsuri : ''}->{$def_ln}) { | 
| 391 | ## TODO: Namespace well-formedness warning? | 
| 392 | last ADD; | 
| 393 | } | 
| 394 | $def_attr_node = $od->create_attribute_ns | 
| 395 | ($def_nsuri, [$def_prefix, $def_ln]); | 
| 396 |  | 
| 397 | for my $child (@{$at->child_nodes}) { | 
| 398 | $def_attr_node->append_child ($child->clone_node (1)); | 
| 399 | } | 
| 400 | $def_attr_node->manakai_attribute_type ($at->declared_type); | 
| 401 | $attr->{defined $def_nsuri ? $def_nsuri : ''}->{$def_ln} | 
| 402 | = $def_attr_node; | 
| 403 | $$def_attr_node->{owner_element} = $_[0]; | 
| 404 | Scalar::Util::weaken ($$def_attr_node->{owner_element}); | 
| 405 | delete $$def_attr_node->{specified}; | 
| 406 | } # ADD | 
| 407 | $cfg->set_parameter | 
| 408 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => $copy_asis); | 
| 409 | } | 
| 410 | } | 
| 411 | } | 
| 412 | } | 
| 413 |  | 
| 414 | return undef; | 
| 415 | } # remove_attribute | 
| 416 |  | 
| 417 | sub remove_attribute_node ($$) { | 
| 418 | my $od = ${$_[0]}->{owner_document}; | 
| 419 | if ($$od->{strict_error_checking} and ${$_[0]}->{manakai_read_only}) { | 
| 420 | report Message::DOM::DOMException | 
| 421 | -object => $_[0], | 
| 422 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 423 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 424 | } | 
| 425 |  | 
| 426 | my $attr_node = $_[1]; | 
| 427 | my $ln = $attr_node->manakai_local_name; | 
| 428 | my $attr = ${$_[0]}->{attributes}; | 
| 429 | FIND: { | 
| 430 | my $nsuri = $attr_node->namespace_uri; | 
| 431 | my $list = $attr->{defined $nsuri ? $nsuri : ''}; | 
| 432 | my $list_node = $list->{$ln}; | 
| 433 | if (defined $list_node and $list_node eq $attr_node) { | 
| 434 | delete $list->{$ln}; | 
| 435 | last FIND; | 
| 436 | } | 
| 437 |  | 
| 438 | report Message::DOM::DOMException | 
| 439 | -object => $_[0], | 
| 440 | -type => 'NOT_FOUND_ERR', | 
| 441 | -subtype => 'NOT_CHILD_ERR'; | 
| 442 | } # FIND | 
| 443 |  | 
| 444 | delete ${$_[0]}->{manakai_content_attribute_list}; | 
| 445 | delete $$attr_node->{owner_element}; | 
| 446 | $$attr_node->{specified} = 1; | 
| 447 |  | 
| 448 | ## Default attribute | 
| 449 | ## Same as |remove_attribute|'s, except where marked as "***". | 
| 450 | local $Error::Depth = $Error::Depth + 1; | 
| 451 | my $cfg = $od->dom_config; | 
| 452 | if ($cfg->get_parameter | 
| 453 | (q<http://suika.fam.cx/www/2006/dom-config/dtd-default-attribute>)) { | 
| 454 | my $doctype = $od->doctype; | 
| 455 | if ($doctype) { | 
| 456 | my $et = $doctype->get_element_type_definition_node | 
| 457 | ($_[0]->manakai_tag_name); | 
| 458 | if ($et) { | 
| 459 | my $at = $et->get_attribute_definition_node ($_[1]->manakai_name); # *** | 
| 460 | if ($at) { | 
| 461 | local $$od->{strict_error_checking} = 0; | 
| 462 | my $copy_asis = $cfg->get_parameter | 
| 463 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>); | 
| 464 | $cfg->set_parameter | 
| 465 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => 1); | 
| 466 | ADD: { | 
| 467 | my $def_attr_node; | 
| 468 | my $def_prefix = $attr_node->prefix; | 
| 469 | my $def_nsuri = ''; | 
| 470 | my $def_ln; | 
| 471 | if (defined $def_prefix) { | 
| 472 | $def_nsuri = | 
| 473 | $def_prefix eq 'xml' ? q<http://www.w3.org/XML/1998/namespace>: | 
| 474 | $def_prefix eq 'xmlns' ? q<http://www.w3.org/2000/xmlns/>: | 
| 475 | $_[0]->lookup_namespace_uri ($def_prefix); | 
| 476 | unless (defined $def_nsuri) { | 
| 477 | ## TODO: Namespace well-formedness error... | 
| 478 | } | 
| 479 | $def_ln = $attr_node->manakai_local_name; | 
| 480 | } else { | 
| 481 | $def_nsuri = $attr_node->manakai_name eq 'xmlns' | 
| 482 | ? q<http://www.w3.org/2000/xmlns/> : undef; | 
| 483 | $def_ln = $attr_node->manakai_local_name; ## *** | 
| 484 | } | 
| 485 | if ($attr->{defined $def_nsuri ? $def_nsuri : ''}->{$def_ln}) { | 
| 486 | ## TODO: Namespace well-formedness warning? | 
| 487 | last ADD; | 
| 488 | } | 
| 489 | $def_attr_node = $od->create_attribute_ns | 
| 490 | ($def_nsuri, [$def_prefix, $def_ln]); | 
| 491 |  | 
| 492 | for my $child (@{$at->child_nodes}) { | 
| 493 | $def_attr_node->append_child ($child->clone_node (1)); | 
| 494 | } | 
| 495 | $def_attr_node->manakai_attribute_type ($at->declared_type); | 
| 496 | $attr->{defined $def_nsuri ? $def_nsuri : ''}->{$def_ln} | 
| 497 | = $def_attr_node; | 
| 498 | $$def_attr_node->{owner_element} = $_[0]; | 
| 499 | Scalar::Util::weaken ($$def_attr_node->{owner_element}); | 
| 500 | delete $$def_attr_node->{specified}; | 
| 501 | } # ADD | 
| 502 | $cfg->set_parameter | 
| 503 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => $copy_asis); | 
| 504 | } | 
| 505 | } | 
| 506 | } | 
| 507 | } | 
| 508 |  | 
| 509 | return $_[1]; | 
| 510 | } # remove_attribute_node | 
| 511 |  | 
| 512 | sub remove_attribute_ns ($$$) { | 
| 513 | my $attr = ${$_[0]}->{attributes}; | 
| 514 |  | 
| 515 | my $list = $attr->{defined $_[1] ? $_[1] : ''}; | 
| 516 | my $key = ''.$_[2]; | 
| 517 | my $attr_node = $list->{$key}; | 
| 518 | return undef unless defined $attr_node; | 
| 519 |  | 
| 520 | ## NOTE: Anything below is same as |remove_attribute|'s except "***" | 
| 521 |  | 
| 522 | my $od = ${$_[0]}->{owner_document}; | 
| 523 | if ($$od->{strict_error_checking} and ${$_[0]}->{manakai_read_only}) { | 
| 524 | report Message::DOM::DOMException | 
| 525 | -object => $_[0], | 
| 526 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 527 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 528 | } | 
| 529 |  | 
| 530 | delete $list->{$key}; | 
| 531 | delete $$attr_node->{owner_element}; | 
| 532 | $$attr_node->{specified} = 1; | 
| 533 | delete ${$_[0]}->{manakai_content_attribute_list}; | 
| 534 |  | 
| 535 | ## Default attribute | 
| 536 | local $Error::Depth = $Error::Depth + 1; | 
| 537 | my $cfg = $od->dom_config; | 
| 538 | if ($cfg->get_parameter | 
| 539 | (q<http://suika.fam.cx/www/2006/dom-config/dtd-default-attribute>)) { | 
| 540 | my $doctype = $od->doctype; | 
| 541 | if ($doctype) { | 
| 542 | my $et = $doctype->get_element_type_definition_node | 
| 543 | ($_[0]->manakai_tag_name); | 
| 544 | if ($et) { | 
| 545 | my $at = $et->get_attribute_definition_node | 
| 546 | ($attr_node->manakai_name); # *** | 
| 547 | if ($at) { | 
| 548 | local $$od->{strict_error_checking} = 0; | 
| 549 | my $copy_asis = $cfg->get_parameter | 
| 550 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>); | 
| 551 | $cfg->set_parameter | 
| 552 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => 1); | 
| 553 | ADD: { | 
| 554 | my $def_attr_node; | 
| 555 | my $def_prefix = $attr_node->prefix; | 
| 556 | my $def_nsuri = ''; | 
| 557 | my $def_ln; | 
| 558 | if (defined $def_prefix) { | 
| 559 | $def_nsuri = | 
| 560 | $def_prefix eq 'xml' ? q<http://www.w3.org/XML/1998/namespace>: | 
| 561 | $def_prefix eq 'xmlns' ? q<http://www.w3.org/2000/xmlns/>: | 
| 562 | $_[0]->lookup_namespace_uri ($def_prefix); | 
| 563 | unless (defined $def_nsuri) { | 
| 564 | ## TODO: Namespace well-formedness error... | 
| 565 | } | 
| 566 | } else { | 
| 567 | $def_nsuri = $attr_node->manakai_name eq 'xmlns' | 
| 568 | ? q<http://www.w3.org/2000/xmlns/> : undef; | 
| 569 | } | 
| 570 | $def_ln = $attr_node->manakai_local_name; # *** | 
| 571 | if ($attr->{defined $def_nsuri ? $def_nsuri : ''}->{$def_ln}) { | 
| 572 | ## TODO: Namespace well-formedness warning? | 
| 573 | last ADD; | 
| 574 | } | 
| 575 | $def_attr_node = $od->create_attribute_ns | 
| 576 | ($def_nsuri, [$def_prefix, $def_ln]); | 
| 577 |  | 
| 578 | for my $child (@{$at->child_nodes}) { | 
| 579 | $def_attr_node->append_child ($child->clone_node (1)); | 
| 580 | } | 
| 581 | $def_attr_node->manakai_attribute_type ($at->declared_type); | 
| 582 | $attr->{defined $def_nsuri ? $def_nsuri : ''}->{$def_ln} | 
| 583 | = $def_attr_node; | 
| 584 | $$def_attr_node->{owner_element} = $_[0]; | 
| 585 | Scalar::Util::weaken ($$def_attr_node->{owner_element}); | 
| 586 | delete $$def_attr_node->{specified}; | 
| 587 | } # ADD | 
| 588 | $cfg->set_parameter | 
| 589 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => $copy_asis); | 
| 590 | } | 
| 591 | } | 
| 592 | } | 
| 593 | } | 
| 594 |  | 
| 595 | return undef; | 
| 596 | } # remove_attribute_ns | 
| 597 |  | 
| 598 | sub set_attribute ($$$) { | 
| 599 | my $od = ${$_[0]}->{owner_document}; | 
| 600 | if ($$od->{strict_error_checking}) { | 
| 601 | if (${$_[0]}->{manakai_read_only}) { | 
| 602 | report Message::DOM::DOMException | 
| 603 | -object => $_[0], | 
| 604 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 605 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 606 | } | 
| 607 | } | 
| 608 |  | 
| 609 | my $name = ''.$_[1]; | 
| 610 | my $attr = ${$_[0]}->{attributes}; | 
| 611 | my $attr_node; | 
| 612 | NS: for my $ns (keys %$attr) { | 
| 613 | for my $ln (keys %{$attr->{$ns}}) { | 
| 614 | my $node = $attr->{$ns}->{$ln}; | 
| 615 | if ($node->manakai_name eq $name) { | 
| 616 | $attr_node = $node; | 
| 617 | last NS; | 
| 618 | } | 
| 619 | } | 
| 620 | } | 
| 621 |  | 
| 622 | local $Error::Depth = $Error::Depth + 1; | 
| 623 | if (defined $attr_node) { | 
| 624 | if ($$od->{strict_error_checking}) { | 
| 625 | $od->create_attribute ($name); # or exception | 
| 626 | } | 
| 627 | } else { | 
| 628 | $attr_node = $od->create_attribute ($name); # return or exception | 
| 629 | delete ${$_[0]}->{manakai_content_attribute_list}; | 
| 630 | $attr->{''}->{$name} = $attr_node; | 
| 631 | $$attr_node->{owner_element} = $_[0]; | 
| 632 | Scalar::Util::weaken ($$attr_node->{owner_element}); | 
| 633 |  | 
| 634 | if ($od->dom_config->get_parameter | 
| 635 | (q<http://suika.fam.cx/www/2006/dom-config/dtd-attribute-type>)) { | 
| 636 | my $doctype = $od->doctype; | 
| 637 | if (defined $doctype) { | 
| 638 | my $et = $doctype->get_element_type_definition_node | 
| 639 | ($_[0]->manakai_tag_name); | 
| 640 | if (defined $et) { | 
| 641 | my $at = $et->get_attribute_definition_node ($attr_node->manakai_name); | 
| 642 | if (defined $at) { | 
| 643 | $attr_node->manakai_attribute_type ($at->declared_type); | 
| 644 | } | 
| 645 | } | 
| 646 | } | 
| 647 | } | 
| 648 | } | 
| 649 |  | 
| 650 | $attr_node->value ($_[2]); # set or exception | 
| 651 | $attr_node->specified (1); | 
| 652 | return undef; | 
| 653 | } # set_attribute | 
| 654 |  | 
| 655 | sub set_attribute_node ($$) { | 
| 656 | my ($self, $new_attr) = @_; | 
| 657 | local $Error::Depth = $Error::Depth + 1; | 
| 658 | my $check = ${$$self->{owner_document}}->{strict_error_checking}; | 
| 659 | if ($check and $$self->{owner_document} ne $new_attr->owner_document) { | 
| 660 | local $Error::Depth = $Error::Depth - 1; | 
| 661 | report Message::DOM::DOMException | 
| 662 | -object => $self, | 
| 663 | -type => 'WRONG_DOCUMENT_ERR'; | 
| 664 | } | 
| 665 |  | 
| 666 | my $nsuri = $$new_attr->{namespace_uri}; | 
| 667 | $nsuri = '' unless defined $nsuri; | 
| 668 | my $ln = $$new_attr->{local_name}; | 
| 669 |  | 
| 670 | delete $$self->{manakai_content_attribute_list}; | 
| 671 | my $attrs = $$self->{attributes}; | 
| 672 | my $current = $attrs->{$nsuri}->{$ln}; | 
| 673 |  | 
| 674 | if (defined $$new_attr->{owner_element}) { | 
| 675 | if (defined $current and $current eq $new_attr) { | 
| 676 | ## No effect | 
| 677 | return undef; # no return value | 
| 678 | } else { | 
| 679 | local $Error::Depth = $Error::Depth - 1; | 
| 680 | report Message::DOM::DOMException | 
| 681 | -object => $self, | 
| 682 | -type => 'INUSE_ATTRIBUTE_ERR'; | 
| 683 | } | 
| 684 | } elsif ($check and $$self->{manakai_read_only}) { | 
| 685 | report Message::DOM::DOMException | 
| 686 | -object => $self, | 
| 687 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 688 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 689 | } | 
| 690 |  | 
| 691 | $attrs->{$nsuri}->{$ln} = $new_attr; | 
| 692 | $$new_attr->{owner_element} = $self; | 
| 693 | Scalar::Util::weaken ($$new_attr->{owner_element}); | 
| 694 | $$new_attr->{specified} = 1; | 
| 695 |  | 
| 696 | if (defined $current) { | 
| 697 | delete $$current->{owner_element}; | 
| 698 | $$current->{specified} = 1; | 
| 699 | } | 
| 700 | return $current; | 
| 701 | } # set_attribute_node | 
| 702 |  | 
| 703 | *set_attribute_node_ns = \&set_attribute_node; | 
| 704 |  | 
| 705 | ## The second parameter only supports manakai extended way | 
| 706 | ## to specify qualified name - "[$prefix, $local_name]" ## TODO: Document | 
| 707 | sub set_attribute_ns ($$$$) { | 
| 708 | my $prefix; | 
| 709 | my $lname; | 
| 710 | if (ref $_[2] eq 'ARRAY') { | 
| 711 | ($prefix, $lname) = @{$_[2]}; | 
| 712 | } else { | 
| 713 | ($prefix, $lname) = split /:/, $_[2], 2; | 
| 714 | ($prefix, $lname) = (undef, $prefix) unless defined $lname; | 
| 715 | } | 
| 716 |  | 
| 717 | my $od = ${$_[0]}->{owner_document}; | 
| 718 | if ($$od->{strict_error_checking}) { | 
| 719 | if (${$_[0]}->{manakai_read_only}) { | 
| 720 | report Message::DOM::DOMException | 
| 721 | -object => $_[0], | 
| 722 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 723 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 724 | } | 
| 725 | } | 
| 726 |  | 
| 727 | my $attr = ${$_[0]}->{attributes}; | 
| 728 | my $attr_node = $attr->{defined $_[1] ? ''.$_[1] : ''}->{$lname}; | 
| 729 |  | 
| 730 | local $Error::Depth = $Error::Depth + 1; | 
| 731 | if (defined $attr_node) { | 
| 732 | if ($$od->{strict_error_checking}) { | 
| 733 | $od->create_attribute_ns ($_[1], [$prefix, $lname]); # name exception | 
| 734 | } | 
| 735 | } else { | 
| 736 | $attr_node = $od->create_attribute_ns | 
| 737 | ($_[1], [$prefix, $lname]); # or exception | 
| 738 | delete ${$_[0]}->{manakai_content_attribute_list}; | 
| 739 | $attr->{defined $_[1] ? ''.$_[1] : ''}->{$lname} = $attr_node; | 
| 740 | $$attr_node->{owner_element} = $_[0]; | 
| 741 | Scalar::Util::weaken ($$attr_node->{owner_element}); | 
| 742 |  | 
| 743 | if ($od->dom_config->get_parameter | 
| 744 | (q<http://suika.fam.cx/www/2006/dom-config/dtd-attribute-type>)) { | 
| 745 | my $doctype = $od->doctype; | 
| 746 | if (defined $doctype) { | 
| 747 | my $et = $doctype->get_element_type_definition_node | 
| 748 | ($_[0]->manakai_tag_name); | 
| 749 | if (defined $et) { | 
| 750 | my $at = $et->get_attribute_definition_node ($attr_node->manakai_name); | 
| 751 | if (defined $at) { | 
| 752 | $attr_node->manakai_attribute_type ($at->declared_type); | 
| 753 | } | 
| 754 | } | 
| 755 | } | 
| 756 | } | 
| 757 | } | 
| 758 |  | 
| 759 | $attr_node->value ($_[3]); # set or exception | 
| 760 | $attr_node->prefix ($prefix); | 
| 761 | $attr_node->specified (1); | 
| 762 | return undef; | 
| 763 | } # set_attribute_ns | 
| 764 |  | 
| 765 | sub set_id_attribute ($$$) { | 
| 766 | if (${${$_[0]}->{owner_document}}->{strict_error_checking} and | 
| 767 | ${$_[0]}->{manakai_read_only}) { | 
| 768 | report Message::DOM::DOMException | 
| 769 | -object => $_[0], | 
| 770 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 771 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 772 | } | 
| 773 |  | 
| 774 | my $attr = $_[0]->get_attribute_node ($_[1]); | 
| 775 | if (not defined $attr) { | 
| 776 | report Message::DOM::DOMException | 
| 777 | -object => $_[0], | 
| 778 | -type => 'NOT_FOUND_ERR', | 
| 779 | -subtype => 'NOT_CHILD_ERR'; | 
| 780 | } else { | 
| 781 | local $Error::Depth = $Error::Depth + 1; | 
| 782 | $attr->is_id ($_[2]); # or exception | 
| 783 | } | 
| 784 | return; | 
| 785 | } # set_id_attribute | 
| 786 |  | 
| 787 | sub set_id_attribute_ns ($$$$) { | 
| 788 | if (${${$_[0]}->{owner_document}}->{strict_error_checking} and | 
| 789 | ${$_[0]}->{manakai_read_only}) { | 
| 790 | report Message::DOM::DOMException | 
| 791 | -object => $_[0], | 
| 792 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 793 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 794 | } | 
| 795 |  | 
| 796 | my $attr = $_[0]->get_attribute_node_ns ($_[1], $_[2]); | 
| 797 | if (not defined $attr) { | 
| 798 | report Message::DOM::DOMException | 
| 799 | -object => $_[0], | 
| 800 | -type => 'NOT_FOUND_ERR', | 
| 801 | -subtype => 'NOT_CHILD_ERR'; | 
| 802 | } else { | 
| 803 | local $Error::Depth = $Error::Depth + 1; | 
| 804 | $attr->is_id ($_[2]); | 
| 805 | } | 
| 806 | return; | 
| 807 | } # set_id_attribute_ns | 
| 808 |  | 
| 809 | sub set_id_attribute_node ($$$$) { | 
| 810 | if (${${$_[0]}->{owner_document}}->{strict_error_checking} and | 
| 811 | ${$_[0]}->{manakai_read_only}) { | 
| 812 | report Message::DOM::DOMException | 
| 813 | -object => $_[0], | 
| 814 | -type => 'NO_MODIFICATION_ALLOWED_ERR', | 
| 815 | -subtype => 'READ_ONLY_NODE_ERR'; | 
| 816 | } | 
| 817 |  | 
| 818 | my $oe = $_[1]->owner_element; | 
| 819 | if ($oe ne $_[0]) { | 
| 820 | report Message::DOM::DOMException | 
| 821 | -object => $_[0], | 
| 822 | -type => 'NOT_FOUND_ERR', | 
| 823 | -subtype => 'NOT_CHILD_ERR'; | 
| 824 | } else { | 
| 825 | local $Error::Depth = $Error::Depth + 1; | 
| 826 | $_[1]->is_id ($_[2]); | 
| 827 | } | 
| 828 | return; | 
| 829 | } # set_id_attribute_node | 
| 830 |  | 
| 831 | ## |ElementCSSInlineStyle| attributes | 
| 832 |  | 
| 833 | sub current_style ($) { | 
| 834 | ## TODO: If not part of document tree | 
| 835 |  | 
| 836 | ## ISSUE: Neither |getComputedStyle| nor |currentStyle| represent | 
| 837 | ## the set of computed values in the real world (in fact what is | 
| 838 | ## represented by them disagree in browsers and even |getComputedStyle| | 
| 839 | ## and |currentStyle| are different in the same Opera browser). | 
| 840 |  | 
| 841 | my $self = shift; | 
| 842 | my $view = $self->owner_document->default_view; | 
| 843 | return undef unless defined $view;  ## ISSUE: Not defined in the spec yet. | 
| 844 |  | 
| 845 | return $view->get_computed_style ($self); | 
| 846 | } # current_style | 
| 847 |  | 
| 848 | ## TODO: |style|, |runtimeStyle| | 
| 849 |  | 
| 850 | package Message::IF::Element; | 
| 851 | package Message::IF::ElementSelector; | 
| 852 | package Message::IF::ElementCSSInlineStyle; | 
| 853 |  | 
| 854 | package Message::DOM::Document; | 
| 855 |  | 
| 856 | sub create_element ($$) { | 
| 857 | my $self = $_[0]; | 
| 858 | if ($$self->{strict_error_checking}) { | 
| 859 | my $xv = $self->xml_version; | 
| 860 | ## TODO: HTML Document ?? | 
| 861 | if (defined $xv) { | 
| 862 | if ($xv eq '1.0' and | 
| 863 | $_[1] =~ /\A\p{InXML_NameStartChar10}\p{InXMLNameChar10}*\z/) { | 
| 864 | # | 
| 865 | } elsif ($xv eq '1.1' and | 
| 866 | $_[1] =~ /\A\p{InXMLNameStartChar11}\p{InXMLNameChar11}*\z/) { | 
| 867 | # | 
| 868 | } else { | 
| 869 | report Message::DOM::DOMException | 
| 870 | -object => $self, | 
| 871 | -type => 'INVALID_CHARACTER_ERR', | 
| 872 | -subtype => 'MALFORMED_NAME_ERR'; | 
| 873 | } | 
| 874 | } | 
| 875 | } | 
| 876 | ## TODO: HTML5 | 
| 877 |  | 
| 878 | my $r = Message::DOM::Element->____new ($self, undef, undef, $_[1]); | 
| 879 |  | 
| 880 | ## -- Default attributes | 
| 881 | { | 
| 882 | local $Error::Depth = $Error::Depth + 1; | 
| 883 | my $cfg = $self->dom_config; | 
| 884 | return $r | 
| 885 | unless $cfg->get_parameter | 
| 886 | (q<http://suika.fam.cx/www/2006/dom-config/dtd-default-attribute>); | 
| 887 |  | 
| 888 | my $doctype = $self->doctype; | 
| 889 | return $r unless defined $doctype; | 
| 890 |  | 
| 891 | my $et = $doctype->get_element_type_definition_node ($_[1]); | 
| 892 | return $r unless defined $et; | 
| 893 |  | 
| 894 | my $orig_strict = $self->strict_error_checking; | 
| 895 | $self->strict_error_checking (0); | 
| 896 |  | 
| 897 | my %gattr; | 
| 898 | my %has_attr; | 
| 899 | my %pfx_to_uri; | 
| 900 | my $copy_asis = $cfg->get_parameter | 
| 901 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>); | 
| 902 | $cfg->set_parameter | 
| 903 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => 1); | 
| 904 |  | 
| 905 | for my $at (@{$et->attribute_definitions}) { | 
| 906 | my $at_default = $at->default_type; | 
| 907 | if ($at_default == 4 or $at_default == 1) { | 
| 908 | # EXPLICIT_DEFAULT, FIXED_DEFAULT | 
| 909 | my ($nn1, $nn2) = split /:/, $at->node_name; | 
| 910 | if (defined $nn2) { # prefixed | 
| 911 | if ($nn1 eq 'xmlns') { | 
| 912 | ## TODO: NCName check, prefix check and NSURI check | 
| 913 | my $attr = $self->create_attribute_ns | 
| 914 | (q<http://www.w3.org/2000/xmlns/>, [$nn1, $nn2]); | 
| 915 | for my $at_child (@{$at->child_nodes}) { | 
| 916 | $attr->append_child ($at_child->clone_node (1)); | 
| 917 | } | 
| 918 | $attr->manakai_attribute_type ($at->declared_type); | 
| 919 | my $nsuri = $attr->value; | 
| 920 | ## TODO: Namespace well-formedness check (NSURI), v1.1 chk | 
| 921 | $pfx_to_uri{$nn2} = $nsuri; | 
| 922 | $r->set_attribute_node_ns ($attr); | 
| 923 | ## NOTE: This method changes |specified| flag | 
| 924 | $attr->specified (0); | 
| 925 | $has_attr{q<http://www.w3.org/2000/xmlns/>}->{$nn2} = 1; | 
| 926 | } else { | 
| 927 | ## TODO: NCName check | 
| 928 | $gattr{$nn1}->{$nn2} = $at; | 
| 929 | } | 
| 930 | } else {            # no prefixed | 
| 931 | my $attr; | 
| 932 | if ($nn1 eq 'xmlns') { | 
| 933 | $attr = $self->create_attribute_ns | 
| 934 | (q<http://www.w3.org/2000/xmlns/>, 'xmlns'); | 
| 935 | $has_attr{q<http://www.w3.org/2000/xmlns/>}->{xmlns} = 1; | 
| 936 | } else { | 
| 937 | $attr = $self->create_attribute_ns (undef, $nn1); | 
| 938 | ## TODO: NCName check | 
| 939 | } | 
| 940 | for my $at_child (@{$at->child_nodes}) { | 
| 941 | $attr->append_child ($at_child->clone_node (1)); | 
| 942 | } | 
| 943 | $attr->manakai_attribute_type ($at->declared_type); | 
| 944 | ## TODO: Namespace well-formedness check (NSURI) | 
| 945 | $r->set_attribute_node_ns ($attr); | 
| 946 | ## NOTE: This method changes |specified| flag | 
| 947 | $attr->specified (0); | 
| 948 | } | 
| 949 | } | 
| 950 | } # attrdefs | 
| 951 | for my $pfx (keys %gattr) { | 
| 952 | my $nsuri = $pfx_to_uri{$pfx}; | 
| 953 | unless (defined $nsuri) { | 
| 954 | ## TODO: Namespace well-formedness error | 
| 955 | } | 
| 956 | LN: for my $ln (keys %{$gattr{$pfx}}) { | 
| 957 | if ($has_attr{defined $nsuri ? $nsuri : ''}->{$ln}) { | 
| 958 | ## TODO: Namespace well-formedness error | 
| 959 | next LN; | 
| 960 | } | 
| 961 | ## TODO: NCName check, prefix check and NSURI check | 
| 962 | my $at = $gattr{$pfx}->{$ln}; | 
| 963 | my $attr = $self->create_attribute_ns ($nsuri, [$pfx, $ln]); | 
| 964 | for my $at_child (@{$at->child_nodes}) { | 
| 965 | $attr->append_child ($at_child->clone_node (1)); | 
| 966 | } | 
| 967 | $attr->manakai_attribute_type ($at->declared_type); | 
| 968 | $r->set_attribute_node_ns ($attr); | 
| 969 | ## NOTE: This method changes |specified| flag | 
| 970 | $attr->specified (0); | 
| 971 | $has_attr{defined $nsuri ? $nsuri : ''}->{$ln} = 1; | 
| 972 | } # LN | 
| 973 | } # pfx | 
| 974 | $cfg->set_parameter | 
| 975 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => $copy_asis); | 
| 976 | $self->strict_error_checking ($orig_strict); | 
| 977 | } | 
| 978 |  | 
| 979 | return $r; | 
| 980 | } # create_element | 
| 981 |  | 
| 982 | sub create_element_ns ($$$) { | 
| 983 | my $self = $_[0]; | 
| 984 | my ($prefix, $lname); | 
| 985 | if (ref $_[2] eq 'ARRAY') { | 
| 986 | ($prefix, $lname) = @{$_[2]}; | 
| 987 | } else { | 
| 988 | ($prefix, $lname) = split /:/, $_[2], 2; | 
| 989 | ($prefix, $lname) = (undef, $prefix) unless defined $lname; | 
| 990 | } | 
| 991 | my $nsuri = defined $_[1] ? $_[1] eq '' ? undef : $_[1] : undef; | 
| 992 |  | 
| 993 | if ($$self->{strict_error_checking}) { | 
| 994 | my $xv = $self->xml_version; | 
| 995 | ## TODO: HTML Document ?? (NOT_SUPPORTED_ERR is different from what Web browsers do) | 
| 996 | if (defined $xv) { | 
| 997 | if ($xv eq '1.0') { | 
| 998 | if (ref $_[2] eq 'ARRAY' or | 
| 999 | $_[2] =~ /\A\p{InXML_NameStartChar10}\p{InXMLNameChar10}*\z/) { | 
| 1000 | if (defined $prefix) { | 
| 1001 | if ($prefix =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) { | 
| 1002 | # | 
| 1003 | } else { | 
| 1004 | report Message::DOM::DOMException | 
| 1005 | -object => $self, | 
| 1006 | -type => 'NAMESPACE_ERR', | 
| 1007 | -subtype => 'MALFORMED_QNAME_ERR'; | 
| 1008 | } | 
| 1009 | } | 
| 1010 | if ($lname =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) { | 
| 1011 | # | 
| 1012 | } else { | 
| 1013 | report Message::DOM::DOMException | 
| 1014 | -object => $self, | 
| 1015 | -type => 'NAMESPACE_ERR', | 
| 1016 | -subtype => 'MALFORMED_QNAME_ERR'; | 
| 1017 | } | 
| 1018 | } else { | 
| 1019 | report Message::DOM::DOMException | 
| 1020 | -object => $self, | 
| 1021 | -type => 'INVALID_CHARACTER_ERR', | 
| 1022 | -subtype => 'MALFORMED_NAME_ERR'; | 
| 1023 | } | 
| 1024 | } elsif ($xv eq '1.1') { | 
| 1025 | if (ref $_[2] eq 'ARRAY' or | 
| 1026 | $_[2] =~ /\A\p{InXML_NameStartChar10}\p{InXMLNameChar10}*\z/) { | 
| 1027 | if (defined $prefix) { | 
| 1028 | if ($prefix =~ /\A\p{InXMLNCNameStartChar11}\p{InXMLNCNameChar11}*\z/) { | 
| 1029 | # | 
| 1030 | } else { | 
| 1031 | report Message::DOM::DOMException | 
| 1032 | -object => $self, | 
| 1033 | -type => 'NAMESPACE_ERR', | 
| 1034 | -subtype => 'MALFORMED_QNAME_ERR'; | 
| 1035 | } | 
| 1036 | } | 
| 1037 | if ($lname =~ /\A\p{InXMLNCNameStartChar11}\p{InXMLNCNameChar11}*\z/) { | 
| 1038 | # | 
| 1039 | } else { | 
| 1040 | report Message::DOM::DOMException | 
| 1041 | -object => $self, | 
| 1042 | -type => 'NAMESPACE_ERR', | 
| 1043 | -subtype => 'MALFORMED_QNAME_ERR'; | 
| 1044 | } | 
| 1045 | } else { | 
| 1046 | report Message::DOM::DOMException | 
| 1047 | -object => $self, | 
| 1048 | -type => 'INVALID_CHARACTER_ERR', | 
| 1049 | -subtype => 'MALFORMED_NAME_ERR'; | 
| 1050 | } | 
| 1051 | } else { | 
| 1052 | die "create_attribute_ns: XML version |$xv| is not supported"; | 
| 1053 | } | 
| 1054 | } | 
| 1055 |  | 
| 1056 | if (defined $prefix) { | 
| 1057 | if (not defined $nsuri) { | 
| 1058 | report Message::DOM::DOMException | 
| 1059 | -object => $self, | 
| 1060 | -type => 'NAMESPACE_ERR', | 
| 1061 | -subtype => 'PREFIXED_NULLNS_ERR'; | 
| 1062 | } elsif ($prefix eq 'xml' and | 
| 1063 | $nsuri ne q<http://www.w3.org/XML/1998/namespace>) { | 
| 1064 | report Message::DOM::DOMException | 
| 1065 | -object => $self, | 
| 1066 | -type => 'NAMESPACE_ERR', | 
| 1067 | -subtype => 'XMLPREFIX_NONXMLNS_ERR'; | 
| 1068 | } elsif ($prefix eq 'xmlns' and | 
| 1069 | $nsuri ne q<http://www.w3.org/2000/xmlns/>) { | 
| 1070 | report Message::DOM::DOMException | 
| 1071 | -object => $self, | 
| 1072 | -type => 'NAMESPACE_ERR', | 
| 1073 | -subtype => 'XMLNSPREFIX_NONXMLNSNS_ERR'; | 
| 1074 | } elsif ($nsuri eq q<http://www.w3.org/2000/xmlns/> and | 
| 1075 | $prefix ne 'xmlns') { | 
| 1076 | report Message::DOM::DOMException | 
| 1077 | -object => $self, | 
| 1078 | -type => 'NAMESPACE_ERR', | 
| 1079 | -subtype => 'NONXMLNSPREFIX_XMLNSNS_ERR'; | 
| 1080 | } | 
| 1081 | } else { # no prefix | 
| 1082 | if ($lname eq 'xmlns' and | 
| 1083 | (not defined $nsuri or $nsuri ne q<http://www.w3.org/2000/xmlns/>)) { | 
| 1084 | report Message::DOM::DOMException | 
| 1085 | -object => $self, | 
| 1086 | -type => 'NAMESPACE_ERR', | 
| 1087 | -subtype => 'XMLNS_NONXMLNSNS_ERR'; | 
| 1088 | } elsif (not defined $nsuri) { | 
| 1089 | # | 
| 1090 | } elsif ($nsuri eq q<http://www.w3.org/2000/xmlns/> and | 
| 1091 | $lname ne 'xmlns') { | 
| 1092 | report Message::DOM::DOMException | 
| 1093 | -object => $self, | 
| 1094 | -type => 'NAMESPACE_ERR', | 
| 1095 | -subtype => 'NONXMLNSPREFIX_XMLNSNS_ERR'; | 
| 1096 | } | 
| 1097 | } | 
| 1098 | } | 
| 1099 |  | 
| 1100 | ## -- Choose the most apppropriate class for the element | 
| 1101 | my $class = 'Message::DOM::Element'; | 
| 1102 | if (defined $nsuri) { | 
| 1103 | if ($nsuri eq q<http://www.w3.org/1999/xhtml>) { | 
| 1104 | require Message::DOM::HTML::HTMLElement; | 
| 1105 | $class = { | 
| 1106 | a => 'Message::DOM::HTML::HTMLAnchorElement', | 
| 1107 | area => 'Message::DOM::HTML::HTMLAreaElement', | 
| 1108 | audio => 'Message::DOM::HTML::HTMLAudioElement', | 
| 1109 | base => 'Message::DOM::HTML::HTMLBaseElement', | 
| 1110 | body => 'Message::DOM::HTML::HTMLBodyElement', | 
| 1111 | canvas => 'Message::DOM::HTML::HTMLCanvasElement', | 
| 1112 | command => 'Message::DOM::HTML::HTMLCommandElement', | 
| 1113 | datagrid => 'Message::DOM::HTML::HTMLDataGridElement', | 
| 1114 | details => 'Message::DOM::HTML::HTMLDetailsElement', | 
| 1115 | embed => 'Message::DOM::HTML::HTMLEmbedElement', | 
| 1116 | 'event-source' => 'Message::DOM::HTML::HTMLEventSourceElement', | 
| 1117 | font => 'Message::DOM::HTML::HTMLFontElement', | 
| 1118 | head => 'Message::DOM::HTML::HTMLHeadElement', | 
| 1119 | html => 'Message::DOM::HTML::HTMLHtmlElement', | 
| 1120 | iframe => 'Message::DOM::HTML::HTMLIFrameElement', | 
| 1121 | img => 'Message::DOM::HTML::HTMLImageElement', | 
| 1122 | li => 'Message::DOM::HTML::HTMLLIElement', | 
| 1123 | link => 'Message::DOM::HTML::HTMLLinkElement', | 
| 1124 | map => 'Message::DOM::HTML::HTMLMapElement', | 
| 1125 | menu => 'Message::DOM::HTML::HTMLMenuElement', | 
| 1126 | meta => 'Message::DOM::HTML::HTMLMetaElement', | 
| 1127 | meter => 'Message::DOM::HTML::HTMLMeterElement', | 
| 1128 | del => 'Message::DOM::HTML::HTMLModElement', | 
| 1129 | ins => 'Message::DOM::HTML::HTMLModElement', | 
| 1130 | object => 'Message::DOM::HTML::HTMLObjectElement', | 
| 1131 | ol => 'Message::DOM::HTML::HTMLOListElement', | 
| 1132 | param => 'Message::DOM::HTML::HTMLParamElement', | 
| 1133 | progress => 'Message::DOM::HTML::HTMLProgressElement', | 
| 1134 | blockquote => 'Message::DOM::HTML::HTMLQuoteElement', | 
| 1135 | q => 'Message::DOM::HTML::HTMLQuoteElement', | 
| 1136 | script => 'Message::DOM::HTML::HTMLScriptElement', | 
| 1137 | source => 'Message::DOM::HTML::HTMLSourceElement', | 
| 1138 | style => 'Message::DOM::HTML::HTMLStyleElement', | 
| 1139 | table => 'Message::DOM::HTML::HTMLTableElement', | 
| 1140 | td => 'Message::DOM::HTML::HTMLTableCellElement', | 
| 1141 | col => 'Message::DOM::HTML::HTMLTableColElement', | 
| 1142 | colgroup => 'Message::DOM::HTML::HTMLTableColElement', | 
| 1143 | th => 'Message::DOM::HTML::HTMLTableHeaderCellElement', | 
| 1144 | tr => 'Message::DOM::HTML::HTMLTableRowElement', | 
| 1145 | tbody => 'Message::DOM::HTML::HTMLTableSectionElement', | 
| 1146 | tfoot => 'Message::DOM::HTML::HTMLTableSectionElement', | 
| 1147 | thead => 'Message::DOM::HTML::HTMLTableSectionElement', | 
| 1148 | time => 'Message::DOM::HTML::HTMLTimeElement', | 
| 1149 | video => 'Message::DOM::HTML::HTMLVideoElement', | 
| 1150 | }->{$lname} || 'Message::DOM::HTML::HTMLElement'; | 
| 1151 | } elsif ($nsuri eq q<http://www.w3.org/2005/Atom>) { | 
| 1152 | require Message::DOM::Atom::AtomElement; | 
| 1153 | $class = { | 
| 1154 | author => 'Message::DOM::Atom::AtomElement::AtomPersonConstruct', | 
| 1155 | category => 'Message::DOM::Atom::AtomElement::AtomCategoryElement', | 
| 1156 | content => 'Message::DOM::Atom::AtomElement::AtomContentElement', | 
| 1157 | contributor => 'Message::DOM::Atom::AtomElement::AtomPersonConstruct', | 
| 1158 | entry => 'Message::DOM::Atom::AtomElement::AtomEntryElement', | 
| 1159 | feed => 'Message::DOM::Atom::AtomElement::AtomFeedElement', | 
| 1160 | generator => 'Message::DOM::Atom::AtomElement::AtomGeneratorElement', | 
| 1161 | link => 'Message::DOM::Atom::AtomElement::AtomLinkElement', | 
| 1162 | published => 'Message::DOM::Atom::AtomElement::AtomDateConstruct', | 
| 1163 | rights => 'Message::DOM::Atom::AtomElement::AtomTextConstruct', | 
| 1164 | source => 'Message::DOM::Atom::AtomElement::AtomSourceElement', | 
| 1165 | subtitle => 'Message::DOM::Atom::AtomElement::AtomTextConstruct', | 
| 1166 | summary => 'Message::DOM::Atom::AtomElement::AtomTextConstruct', | 
| 1167 | title => 'Message::DOM::Atom::AtomElement::AtomTextConstruct', | 
| 1168 | updated => 'Message::DOM::Atom::AtomElement::AtomDateConstruct', | 
| 1169 | }->{$lname} || 'Message::DOM::Atom::AtomElement'; | 
| 1170 | } | 
| 1171 | } | 
| 1172 |  | 
| 1173 | my $r = $class->____new ($self, $nsuri, $prefix, $lname); | 
| 1174 |  | 
| 1175 | ## -- Default attributes | 
| 1176 | { | 
| 1177 | local $Error::Depth = $Error::Depth + 1; | 
| 1178 | my $cfg = $self->dom_config; | 
| 1179 | return $r | 
| 1180 | unless $cfg->get_parameter | 
| 1181 | (q<http://suika.fam.cx/www/2006/dom-config/dtd-default-attribute>); | 
| 1182 |  | 
| 1183 | my $doctype = $self->doctype; | 
| 1184 | return $r unless defined $doctype; | 
| 1185 |  | 
| 1186 | my $et = $doctype->get_element_type_definition_node | 
| 1187 | (defined $prefix ? $prefix . ':' . $lname : $lname); | 
| 1188 | return $r unless defined $et; | 
| 1189 |  | 
| 1190 | my $orig_strict = $self->strict_error_checking; | 
| 1191 | $self->strict_error_checking (0); | 
| 1192 |  | 
| 1193 | my %gattr; | 
| 1194 | my %has_attr; | 
| 1195 | my %pfx_to_uri; | 
| 1196 | my $copy_asis = $cfg->get_parameter | 
| 1197 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>); | 
| 1198 | $cfg->set_parameter | 
| 1199 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => 1); | 
| 1200 |  | 
| 1201 | for my $at (@{$et->attribute_definitions}) { | 
| 1202 | my $at_default = $at->default_type; | 
| 1203 | if ($at_default == 4 or $at_default == 1) { | 
| 1204 | # EXPLICIT_DEFAULT, FIXED_DEFAULT | 
| 1205 | my ($nn1, $nn2) = split /:/, $at->node_name; | 
| 1206 | if (defined $nn2) { # prefixed | 
| 1207 | if ($nn1 eq 'xmlns') { | 
| 1208 | ## TODO: NCName check, prefix check and NSURI check | 
| 1209 | my $attr = $self->create_attribute_ns | 
| 1210 | (q<http://www.w3.org/2000/xmlns/>, [$nn1, $nn2]); | 
| 1211 | for my $at_child (@{$at->child_nodes}) { | 
| 1212 | $attr->append_child ($at_child->clone_node (1)); | 
| 1213 | } | 
| 1214 | $attr->manakai_attribute_type ($at->declared_type); | 
| 1215 | my $nsuri = $attr->value; | 
| 1216 | ## TODO: Namespace well-formedness check (NSURI), v1.1 chk | 
| 1217 | $pfx_to_uri{$nn2} = $nsuri; | 
| 1218 | $r->set_attribute_node_ns ($attr); | 
| 1219 | ## NOTE: This method changes |specified| flag | 
| 1220 | $attr->specified (0); | 
| 1221 | $has_attr{q<http://www.w3.org/2000/xmlns/>}->{$nn2} = 1; | 
| 1222 | } else { | 
| 1223 | ## TODO: NCName check | 
| 1224 | $gattr{$nn1}->{$nn2} = $at; | 
| 1225 | } | 
| 1226 | } else {            # no prefixed | 
| 1227 | my $attr; | 
| 1228 | if ($nn1 eq 'xmlns') { | 
| 1229 | $attr = $self->create_attribute_ns | 
| 1230 | (q<http://www.w3.org/2000/xmlns/>, 'xmlns'); | 
| 1231 | $has_attr{q<http://www.w3.org/2000/xmlns/>}->{xmlns} = 1; | 
| 1232 | } else { | 
| 1233 | $attr = $self->create_attribute_ns (undef, $nn1); | 
| 1234 | ## TODO: NCName check | 
| 1235 | } | 
| 1236 | for my $at_child (@{$at->child_nodes}) { | 
| 1237 | $attr->append_child ($at_child->clone_node (1)); | 
| 1238 | } | 
| 1239 | $attr->manakai_attribute_type ($at->declared_type); | 
| 1240 | ## TODO: Namespace well-formedness check (NSURI) | 
| 1241 | $r->set_attribute_node_ns ($attr); | 
| 1242 | ## NOTE: This method changes |specified| flag | 
| 1243 | $attr->specified (0); | 
| 1244 | } | 
| 1245 | } | 
| 1246 | } # attrdefs | 
| 1247 | for my $pfx (keys %gattr) { | 
| 1248 | my $nsuri = $pfx_to_uri{$pfx}; | 
| 1249 | unless (defined $nsuri) { | 
| 1250 | ## TODO: Namespace well-formedness error | 
| 1251 | } | 
| 1252 | LN: for my $ln (keys %{$gattr{$pfx}}) { | 
| 1253 | if ($has_attr{defined $nsuri ? $nsuri : ''}->{$ln}) { | 
| 1254 | ## TODO: Namespace well-formedness error | 
| 1255 | next LN; | 
| 1256 | } | 
| 1257 | ## TODO: NCName check, prefix check and NSURI check | 
| 1258 | my $at = $gattr{$pfx}->{$ln}; | 
| 1259 | my $attr = $self->create_attribute_ns ($nsuri, [$pfx, $ln]); | 
| 1260 | for my $at_child (@{$at->child_nodes}) { | 
| 1261 | $attr->append_child ($at_child->clone_node (1)); | 
| 1262 | } | 
| 1263 | $attr->manakai_attribute_type ($at->declared_type); | 
| 1264 | $r->set_attribute_node_ns ($attr); | 
| 1265 | ## NOTE: This method changes |specified| flag | 
| 1266 | $attr->specified (0); | 
| 1267 | $has_attr{defined $nsuri ? $nsuri : ''}->{$ln} = 1; | 
| 1268 | } # LN | 
| 1269 | } # pfx | 
| 1270 | $cfg->set_parameter | 
| 1271 | (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree> => $copy_asis); | 
| 1272 | $self->strict_error_checking ($orig_strict); | 
| 1273 | } | 
| 1274 |  | 
| 1275 | return $r; | 
| 1276 | } # create_element_ns | 
| 1277 |  | 
| 1278 | =head1 LICENSE | 
| 1279 |  | 
| 1280 | Copyright 2007 Wakaba <w@suika.fam.cx> | 
| 1281 |  | 
| 1282 | This program is free software; you can redistribute it and/or | 
| 1283 | modify it under the same terms as Perl itself. | 
| 1284 |  | 
| 1285 | =cut | 
| 1286 |  | 
| 1287 | 1; | 
| 1288 | ## $Date: 2007/11/11 04:23:32 $ |