279 |
|
|
280 |
## |Node| methods |
## |Node| methods |
281 |
|
|
282 |
|
sub append_child ($$) { |
283 |
|
## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|. |
284 |
|
## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|, |
285 |
|
## |Notation|, |ProcessingInstruction| |ElementTypeDefinition|, |
286 |
|
## and |DocumentType| define their own implementations. |
287 |
|
my $self = $_[0]; |
288 |
|
|
289 |
|
## NOTE: Depends on $self->node_type: |
290 |
|
my $self_od = $$self->{owner_document}; |
291 |
|
|
292 |
|
## -- Node Type check |
293 |
|
my @new_child; |
294 |
|
my $new_child_parent; |
295 |
|
if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) { |
296 |
|
push @new_child, @{$_[1]->child_nodes}; |
297 |
|
$new_child_parent = $_[1]; |
298 |
|
} else { |
299 |
|
@new_child = ($_[1]); |
300 |
|
$new_child_parent = $_[1]->parent_node; |
301 |
|
} |
302 |
|
|
303 |
|
## NOTE: Depends on $self->node_type: |
304 |
|
if ($$self_od->{strict_error_checking}) { |
305 |
|
my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType |
306 |
|
if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) { |
307 |
|
report Message::DOM::DOMException |
308 |
|
-object => $self, |
309 |
|
-type => 'WRONG_DOCUMENT_ERR', |
310 |
|
-subtype => 'EXTERNAL_OBJECT_ERR'; |
311 |
|
} |
312 |
|
|
313 |
|
if ($$self->{manakai_read_only} or |
314 |
|
(@new_child and defined $new_child_parent and |
315 |
|
$$new_child_parent->{manakai_read_only})) { |
316 |
|
report Message::DOM::DOMException |
317 |
|
-object => $self, |
318 |
|
-type => 'NO_MODIFICATION_ALLOWED_ERR', |
319 |
|
-subtype => 'READ_ONLY_NODE_ERR'; |
320 |
|
} |
321 |
|
|
322 |
|
## NOTE: |Document| has children order check here. |
323 |
|
|
324 |
|
for my $cn (@new_child) { |
325 |
|
unless ({ |
326 |
|
TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1, |
327 |
|
ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1, |
328 |
|
PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1, |
329 |
|
}->{$cn->node_type}) { |
330 |
|
report Message::DOM::DOMException |
331 |
|
-object => $self, |
332 |
|
-type => 'HIERARCHY_REQUEST_ERR', |
333 |
|
-subtype => 'CHILD_NODE_TYPE_ERR'; |
334 |
|
} |
335 |
|
} |
336 |
|
|
337 |
|
my $anode = $self; |
338 |
|
while (defined $anode) { |
339 |
|
if ($anode eq $_[1]) { |
340 |
|
report Message::DOM::DOMException |
341 |
|
-object => $self, |
342 |
|
-type => 'HIERARCHY_REQUEST_ERR', |
343 |
|
-subtype => 'ANCESTOR_NODE_ERR'; |
344 |
|
} |
345 |
|
$anode = $$anode->{parent_node}; |
346 |
|
} |
347 |
|
} |
348 |
|
|
349 |
|
## NOTE: "Insert at" code only in insert_before and replace_child |
350 |
|
|
351 |
|
## -- Removes from parent |
352 |
|
if ($new_child_parent) { |
353 |
|
if (@new_child == 1) { |
354 |
|
my $v = $$new_child_parent->{child_nodes}; |
355 |
|
RP: for my $i (0..$#$v) { |
356 |
|
if ($v->[$i] eq $new_child[0]) { |
357 |
|
splice @$v, $i, 1, (); |
358 |
|
last RP; |
359 |
|
} |
360 |
|
} # RP |
361 |
|
} else { |
362 |
|
@{$$new_child_parent->{child_nodes}} = (); |
363 |
|
} |
364 |
|
} |
365 |
|
|
366 |
|
## -- Rewrite the |parentNode| properties |
367 |
|
for my $nc (@new_child) { |
368 |
|
$$nc->{parent_node} = $self; |
369 |
|
Scalar::Util::weaken ($$nc->{parent_node}); |
370 |
|
} |
371 |
|
|
372 |
|
## NOTE: Depends on method: |
373 |
|
push @{$$self->{child_nodes}}, @new_child; |
374 |
|
|
375 |
|
## NOTE: Setting |owner_document| in |Document|. |
376 |
|
|
377 |
|
return $_[1]; |
378 |
|
} # apepnd_child |
379 |
|
|
380 |
sub clone_node ($;$) { |
sub clone_node ($;$) { |
381 |
my ($self, $deep) = @_; |
my ($self, $deep) = @_; |
382 |
|
|
704 |
return (@{${$_[0]}->{child_nodes} or []} > 0); |
return (@{${$_[0]}->{child_nodes} or []} > 0); |
705 |
} # has_child_nodes |
} # has_child_nodes |
706 |
|
|
707 |
|
sub insert_before ($$) { |
708 |
|
## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|. |
709 |
|
## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|, |
710 |
|
## |Notation|, |ProcessingInstruction|, |ElementTypeDefinition|, |
711 |
|
## and |DocumentType| define their own implementations. |
712 |
|
my $self = $_[0]; |
713 |
|
|
714 |
|
## NOTE: Depends on $self->node_type: |
715 |
|
my $self_od = $$self->{owner_document}; |
716 |
|
|
717 |
|
## -- Node Type check |
718 |
|
my @new_child; |
719 |
|
my $new_child_parent; |
720 |
|
if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) { |
721 |
|
push @new_child, @{$_[1]->child_nodes}; |
722 |
|
$new_child_parent = $_[1]; |
723 |
|
} else { |
724 |
|
@new_child = ($_[1]); |
725 |
|
$new_child_parent = $_[1]->parent_node; |
726 |
|
} |
727 |
|
|
728 |
|
## NOTE: Depends on $self->node_type: |
729 |
|
if ($$self_od->{strict_error_checking}) { |
730 |
|
my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType |
731 |
|
if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) { |
732 |
|
report Message::DOM::DOMException |
733 |
|
-object => $self, |
734 |
|
-type => 'WRONG_DOCUMENT_ERR', |
735 |
|
-subtype => 'EXTERNAL_OBJECT_ERR'; |
736 |
|
} |
737 |
|
|
738 |
|
if ($$self->{manakai_read_only} or |
739 |
|
(@new_child and defined $new_child_parent and |
740 |
|
$$new_child_parent->{manakai_read_only})) { |
741 |
|
report Message::DOM::DOMException |
742 |
|
-object => $self, |
743 |
|
-type => 'NO_MODIFICATION_ALLOWED_ERR', |
744 |
|
-subtype => 'READ_ONLY_NODE_ERR'; |
745 |
|
} |
746 |
|
|
747 |
|
## NOTE: |Document| has children order check here. |
748 |
|
|
749 |
|
for my $cn (@new_child) { |
750 |
|
unless ({ |
751 |
|
TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1, |
752 |
|
ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1, |
753 |
|
PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1, |
754 |
|
}->{$cn->node_type}) { |
755 |
|
report Message::DOM::DOMException |
756 |
|
-object => $self, |
757 |
|
-type => 'HIERARCHY_REQUEST_ERR', |
758 |
|
-subtype => 'CHILD_NODE_TYPE_ERR'; |
759 |
|
} |
760 |
|
} |
761 |
|
|
762 |
|
my $anode = $self; |
763 |
|
while (defined $anode) { |
764 |
|
if ($anode eq $_[1]) { |
765 |
|
report Message::DOM::DOMException |
766 |
|
-object => $self, |
767 |
|
-type => 'HIERARCHY_REQUEST_ERR', |
768 |
|
-subtype => 'ANCESTOR_NODE_ERR'; |
769 |
|
} |
770 |
|
$anode = $$anode->{parent_node}; |
771 |
|
} |
772 |
|
} |
773 |
|
|
774 |
|
## -- Insert at... ## NOTE: Only in insert_before and replace_child |
775 |
|
my $index = -1; # last |
776 |
|
if (defined $_[2]) { |
777 |
|
## error if $_[1] eq $_[2]; |
778 |
|
|
779 |
|
my $cns = $self->child_nodes; |
780 |
|
my $cnsl = @$cns; |
781 |
|
C: { |
782 |
|
$index = 0; |
783 |
|
for my $i (0..($cnsl-1)) { |
784 |
|
my $cn = $cns->[$i]; |
785 |
|
if ($cn eq $_[2]) { |
786 |
|
$index += $i; |
787 |
|
last C; |
788 |
|
} elsif ($cn eq $_[1]) { |
789 |
|
$index = -1; # offset |
790 |
|
} |
791 |
|
} |
792 |
|
|
793 |
|
report Message::DOM::DOMException |
794 |
|
-object => $self, |
795 |
|
-type => 'NOT_FOUND_ERR', |
796 |
|
-subtype => 'NOT_CHILD_ERR'; |
797 |
|
} # C |
798 |
|
} |
799 |
|
## NOTE: "else" only in replace_child |
800 |
|
|
801 |
|
## -- Removes from parent |
802 |
|
if ($new_child_parent) { |
803 |
|
if (@new_child == 1) { |
804 |
|
my $v = $$new_child_parent->{child_nodes}; |
805 |
|
RP: for my $i (0..$#$v) { |
806 |
|
if ($v->[$i] eq $new_child[0]) { |
807 |
|
splice @$v, $i, 1, (); |
808 |
|
last RP; |
809 |
|
} |
810 |
|
} # RP |
811 |
|
} else { |
812 |
|
@{$$new_child_parent->{child_nodes}} = (); |
813 |
|
} |
814 |
|
} |
815 |
|
|
816 |
|
## -- Rewrite the |parentNode| properties |
817 |
|
for my $nc (@new_child) { |
818 |
|
$$nc->{parent_node} = $self; |
819 |
|
Scalar::Util::weaken ($$nc->{parent_node}); |
820 |
|
} |
821 |
|
|
822 |
|
## NOTE: Depends on method: |
823 |
|
if ($index == -1) { |
824 |
|
push @{$$self->{child_nodes}}, @new_child; |
825 |
|
} else { |
826 |
|
splice @{$$self->{child_nodes}}, $index, 0, @new_child; |
827 |
|
} |
828 |
|
|
829 |
|
## NOTE: Setting |owner_document| in |Document|. |
830 |
|
|
831 |
|
return $_[1]; |
832 |
|
} # insert_before |
833 |
|
|
834 |
sub is_equal_node ($$) { |
sub is_equal_node ($$) { |
835 |
local $Error::Depth = $Error::Depth + 1; |
local $Error::Depth = $Error::Depth + 1; |
836 |
|
|
882 |
return $Message::DOM::DOMImplementation::HasFeature->{$feature}->{$version}; |
return $Message::DOM::DOMImplementation::HasFeature->{$feature}->{$version}; |
883 |
} # is_supported; |
} # is_supported; |
884 |
|
|
|
## NOTE: Only applied to Elements and Documents |
|
|
sub append_child ($$) { |
|
|
my ($self, $new_child) = @_; |
|
|
if (defined $$new_child->{parent_node}) { |
|
|
my $parent_list = ${$$new_child->{parent_node}}->{child_nodes}; |
|
|
for (0..$#$parent_list) { |
|
|
if ($parent_list->[$_] eq $new_child) { |
|
|
splice @$parent_list, $_, 1; |
|
|
last; |
|
|
} |
|
|
} |
|
|
} |
|
|
push @{$$self->{child_nodes}}, $new_child; |
|
|
$$new_child->{parent_node} = $self; |
|
|
Scalar::Util::weaken ($$new_child->{parent_node}); |
|
|
## TODO: |
|
|
$$new_child->{owner_document} = $self if $self->node_type == DOCUMENT_NODE; |
|
|
return $new_child; |
|
|
} # append_child |
|
|
|
|
885 |
sub manakai_append_text ($$) { |
sub manakai_append_text ($$) { |
886 |
## NOTE: For |Element|, |Attr|, |Entity|, |EntityReference|, |
## NOTE: For |Element|, |Attr|, |Entity|, |EntityReference|, |
887 |
## |DocumentFragment|, and |AttributeDefinition|. In addition, |
## |DocumentFragment|, and |AttributeDefinition|. In addition, |
901 |
} |
} |
902 |
} # manakai_append_text |
} # manakai_append_text |
903 |
|
|
|
## NOTE: Only applied to Elements and Documents |
|
|
sub insert_before ($$;$) { |
|
|
my ($self, $new_child, $ref_child) = @_; |
|
|
if (defined $$new_child->{parent_node}) { |
|
|
my $parent_list = ${$$new_child->{parent_node}}->{child_nodes}; |
|
|
for (0..$#$parent_list) { |
|
|
if ($parent_list->[$_] eq $new_child) { |
|
|
splice @$parent_list, $_, 1; |
|
|
last; |
|
|
} |
|
|
} |
|
|
} |
|
|
my $i = @{$$self->{child_nodes}}; |
|
|
if (defined $ref_child) { |
|
|
for (0..$#{$$self->{child_nodes}}) { |
|
|
if ($$self->{child_nodes}->[$_] eq $ref_child) { |
|
|
$i = $_; |
|
|
last; |
|
|
} |
|
|
} |
|
|
} |
|
|
splice @{$$self->{child_nodes}}, $i, 0, $new_child; |
|
|
$$new_child->{parent_node} = $self; |
|
|
Scalar::Util::weaken ($$new_child->{parent_node}); |
|
|
return $new_child; |
|
|
} # insert_before |
|
|
|
|
904 |
sub is_default_namespace ($$) { |
sub is_default_namespace ($$) { |
905 |
## TODO: Document that ElementTypeDefinition and AttributeDefinition |
## TODO: Document that ElementTypeDefinition and AttributeDefinition |
906 |
## are same as DocumentType |
## are same as DocumentType |
1111 |
## is not reverted. |
## is not reverted. |
1112 |
} # normalize |
} # normalize |
1113 |
|
|
|
## NOTE: Only applied to Elements and Documents |
|
1114 |
sub remove_child ($$) { |
sub remove_child ($$) { |
1115 |
my ($self, $old_child) = @_; |
my ($self, $old_child) = @_; |
1116 |
my $parent_list = $$self->{child_nodes}; |
|
1117 |
|
if ($$self->{manakai_read_only} and |
1118 |
|
${$$self->{owner_document} or $self}->{strict_error_checking}) { |
1119 |
|
report Message::DOM::DOMException |
1120 |
|
-object => $self, |
1121 |
|
-type => 'NO_MODIFICATION_ALLOWED_ERR', |
1122 |
|
-subtype => 'READ_ONLY_NODE_ERR'; |
1123 |
|
} |
1124 |
|
|
1125 |
|
my $parent_list = $$self->{child_nodes} || []; |
1126 |
for (0..$#$parent_list) { |
for (0..$#$parent_list) { |
1127 |
if ($parent_list->[$_] eq $old_child) { |
if ($parent_list->[$_] eq $old_child) { |
1128 |
splice @$parent_list, $_, 1; |
splice @$parent_list, $_, 1, (); |
1129 |
last; |
delete $$old_child->{parent_node}; |
1130 |
|
return $old_child; |
1131 |
} |
} |
1132 |
} |
} |
1133 |
delete $$old_child->{parent_node}; |
|
1134 |
return $old_child; |
report Message::DOM::DOMException |
1135 |
|
-object => $self, |
1136 |
|
-type => 'NOT_FOUND_ERR', |
1137 |
|
-subtype => 'NOT_CHILD_ERR'; |
1138 |
} # remove_child |
} # remove_child |
1139 |
|
|
1140 |
|
sub replace_child ($$) { |
1141 |
|
## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|. |
1142 |
|
## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|, |
1143 |
|
## |Notation|, |ProcessingInstruction|, |ElementTypeDefinition|, |
1144 |
|
## and |DocumentType| define their own implementations. |
1145 |
|
my $self = $_[0]; |
1146 |
|
|
1147 |
|
## NOTE: Depends on $self->node_type: |
1148 |
|
my $self_od = $$self->{owner_document}; |
1149 |
|
|
1150 |
|
## -- Node Type check |
1151 |
|
my @new_child; |
1152 |
|
my $new_child_parent; |
1153 |
|
if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) { |
1154 |
|
push @new_child, @{$_[1]->child_nodes}; |
1155 |
|
$new_child_parent = $_[1]; |
1156 |
|
} else { |
1157 |
|
@new_child = ($_[1]); |
1158 |
|
$new_child_parent = $_[1]->parent_node; |
1159 |
|
} |
1160 |
|
|
1161 |
|
## NOTE: Depends on $self->node_type: |
1162 |
|
if ($$self_od->{strict_error_checking}) { |
1163 |
|
my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType |
1164 |
|
if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) { |
1165 |
|
report Message::DOM::DOMException |
1166 |
|
-object => $self, |
1167 |
|
-type => 'WRONG_DOCUMENT_ERR', |
1168 |
|
-subtype => 'EXTERNAL_OBJECT_ERR'; |
1169 |
|
} |
1170 |
|
|
1171 |
|
if ($$self->{manakai_read_only} or |
1172 |
|
(@new_child and defined $new_child_parent and |
1173 |
|
$$new_child_parent->{manakai_read_only})) { |
1174 |
|
report Message::DOM::DOMException |
1175 |
|
-object => $self, |
1176 |
|
-type => 'NO_MODIFICATION_ALLOWED_ERR', |
1177 |
|
-subtype => 'READ_ONLY_NODE_ERR'; |
1178 |
|
} |
1179 |
|
|
1180 |
|
## NOTE: |Document| has children order check here. |
1181 |
|
|
1182 |
|
for my $cn (@new_child) { |
1183 |
|
unless ({ |
1184 |
|
TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1, |
1185 |
|
ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1, |
1186 |
|
PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1, |
1187 |
|
}->{$cn->node_type}) { |
1188 |
|
report Message::DOM::DOMException |
1189 |
|
-object => $self, |
1190 |
|
-type => 'HIERARCHY_REQUEST_ERR', |
1191 |
|
-subtype => 'CHILD_NODE_TYPE_ERR'; |
1192 |
|
} |
1193 |
|
} |
1194 |
|
|
1195 |
|
my $anode = $self; |
1196 |
|
while (defined $anode) { |
1197 |
|
if ($anode eq $_[1]) { |
1198 |
|
report Message::DOM::DOMException |
1199 |
|
-object => $self, |
1200 |
|
-type => 'HIERARCHY_REQUEST_ERR', |
1201 |
|
-subtype => 'ANCESTOR_NODE_ERR'; |
1202 |
|
} |
1203 |
|
$anode = $$anode->{parent_node}; |
1204 |
|
} |
1205 |
|
} |
1206 |
|
|
1207 |
|
## -- Insert at... ## NOTE: Only in insertBefore and replaceChild |
1208 |
|
my $index = -1; # last |
1209 |
|
if (defined $_[2]) { |
1210 |
|
## error if $_[1] eq $_[2]; |
1211 |
|
|
1212 |
|
my $cns = $self->child_nodes; |
1213 |
|
my $cnsl = @$cns; |
1214 |
|
C: { |
1215 |
|
$index = 0; |
1216 |
|
for my $i (0..($cnsl-1)) { |
1217 |
|
my $cn = $cns->[$i]; |
1218 |
|
if ($cn eq $_[2]) { |
1219 |
|
$index += $i; |
1220 |
|
last C; |
1221 |
|
} elsif ($cn eq $_[1]) { |
1222 |
|
$index = -1; # offset |
1223 |
|
} |
1224 |
|
} |
1225 |
|
|
1226 |
|
report Message::DOM::DOMException |
1227 |
|
-object => $self, |
1228 |
|
-type => 'NOT_FOUND_ERR', |
1229 |
|
-subtype => 'NOT_CHILD_ERR'; |
1230 |
|
} # C |
1231 |
|
} else { |
1232 |
|
## NOTE: Only in replaceChild |
1233 |
|
report Message::DOM::DOMException |
1234 |
|
-object => $self, |
1235 |
|
-type => 'NOT_FOUND_ERR', |
1236 |
|
-subtype => 'NOT_CHILD_ERR'; |
1237 |
|
} |
1238 |
|
|
1239 |
|
## -- Removes from parent |
1240 |
|
if ($new_child_parent) { |
1241 |
|
if (@new_child == 1) { |
1242 |
|
my $v = $$new_child_parent->{child_nodes}; |
1243 |
|
RP: for my $i (0..$#$v) { |
1244 |
|
if ($v->[$i] eq $new_child[0]) { |
1245 |
|
splice @$v, $i, 1, (); |
1246 |
|
last RP; |
1247 |
|
} |
1248 |
|
} # RP |
1249 |
|
} else { |
1250 |
|
@{$$new_child_parent->{child_nodes}} = (); |
1251 |
|
} |
1252 |
|
} |
1253 |
|
|
1254 |
|
## -- Rewrite the |parentNode| properties |
1255 |
|
for my $nc (@new_child) { |
1256 |
|
$$nc->{parent_node} = $self; |
1257 |
|
Scalar::Util::weaken ($$nc->{parent_node}); |
1258 |
|
} |
1259 |
|
|
1260 |
|
## NOTE: Depends on method: |
1261 |
|
splice @{$$self->{child_nodes}}, $index, 1, @new_child; |
1262 |
|
delete ${$_[2]}->{parent_node}; |
1263 |
|
|
1264 |
|
## NOTE: Setting |owner_document| in |Document|. |
1265 |
|
|
1266 |
|
return $_[2]; |
1267 |
|
} # replace_child |
1268 |
|
|
1269 |
sub manakai_set_read_only ($;$$) { |
sub manakai_set_read_only ($;$$) { |
1270 |
my $value = 1 if $_[1]; |
my $value = 1 if $_[1]; |
1271 |
if ($_[2]) { |
if ($_[2]) { |
1320 |
|
|
1321 |
if (defined $handler) { |
if (defined $handler) { |
1322 |
eval q{ |
eval q{ |
1323 |
|
no warnings; |
1324 |
sub DESTROY { |
sub DESTROY { |
1325 |
my $uds = ${$_[0]}->{user_data}; |
my $uds = ${$_[0]}->{user_data}; |
1326 |
for my $key (keys %$uds) { |
for my $key (keys %$uds) { |