/[suikacvs]/messaging/manakai/lib/Message/DOM/Node.pm
Suika

Contents of /messaging/manakai/lib/Message/DOM/Node.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.19 - (show annotations) (download)
Tue Oct 21 07:51:59 2008 UTC (16 years, 1 month ago) by wakaba
Branch: MAIN
CVS Tags: HEAD
Changes since 1.18: +65 -2 lines
++ ChangeLog	21 Oct 2008 07:51:49 -0000
2008-10-21  Wakaba  <wakaba@suika.fam.cx>

	* cvscommit.sh: Invoke |mkcommitfeed.pl|.

	* mkcommitfeed.pl: New script (copied from Whatpm repository).

++ manakai/lib/Message/DOM/ChangeLog	21 Oct 2008 07:48:11 -0000
2008-10-21  Wakaba  <wakaba@suika.fam.cx>

	* Document.pm (inner_html): Use Whatpm::XML::Parser for XML
	parsing.

	* Node.pm (manakai_html_language): New attribute.

1 package Message::DOM::Node;
2 use strict;
3 our $VERSION=do{my @r=(q$Revision: 1.18 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 push our @ISA, 'Message::IF::Node',
5 'Message::IF::NSResolver';
6 require Scalar::Util;
7 require Message::DOM::DOMException;
8
9 ## NOTE:
10 ## Node
11 ## + Attr (2)
12 ## + AttributeDefinition (81002)
13 ## + CharacterData
14 ## + Comment (8)
15 ## + Text (3)
16 ## + CDATASection (4)
17 ## + Document (9)
18 ## + DocumentFragment (11)
19 ## + DocumentType (10)
20 ## + Element (1)
21 ## + ElementTypeDefinition (81001)
22 ## + Entity (6)
23 ## + EntityReference (5)
24 ## + Notation (12)
25 ## + ProcessingInstruction (7)
26
27 use overload
28 '==' => 'is_equal_node',
29 '!=' => sub {
30 return not ($_[0] == $_[1]);
31 },
32 #eq => sub { $_[0] eq $_[1] }, ## is_same_node
33 #ne => sub { $_[0] ne $_[1] }, ## not is_same_node
34 fallback => 1;
35
36 ## The |Node| interface - constants
37
38 ## Definition group NodeType
39
40 ## NOTE: Numeric codes up to 200 are reserved by W3C [DOM1SE, DOM2, DOM3].
41
42 sub ELEMENT_NODE () { 1 }
43 sub ATTRIBUTE_NODE () { 2 }
44 sub TEXT_NODE () { 3 }
45 sub CDATA_SECTION_NODE () { 4 }
46 sub ENTITY_REFERENCE_NODE () { 5 }
47 sub ENTITY_NODE () { 6 }
48 sub PROCESSING_INSTRUCTION_NODE () { 7 }
49 sub COMMENT_NODE () { 8 }
50 sub DOCUMENT_NODE () { 9 }
51 sub DOCUMENT_TYPE_NODE () { 10 }
52 sub DOCUMENT_FRAGMENT_NODE () { 11 }
53 sub NOTATION_NODE () { 12 }
54 sub ELEMENT_TYPE_DEFINITION_NODE () { 81001 }
55 sub ATTRIBUTE_DEFINITION_NODE () { 81002 }
56
57 ## Definition group DocumentPosition
58
59 sub DOCUMENT_POSITION_DISCONNECTED () { 0x01 }
60 sub DOCUMENT_POSITION_PRECEDING () { 0x02 }
61 sub DOCUMENT_POSITION_FOLLOWING () { 0x04 }
62 sub DOCUMENT_POSITION_CONTAINS () { 0x08 }
63 sub DOCUMENT_POSITION_CONTAINED_BY () { 0x10 }
64 sub DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC () { 0x20 }
65
66 ## |OperationType| from the |UserDataHandler| interface.
67 sub NODE_CLONED () { 1 }
68 sub NODE_IMPORTED () { 2 }
69 sub NODE_DELETED () { 3 }
70 sub NODE_RENAMED () { 4 }
71 sub NODE_ADOPTED () { 5 }
72
73 sub ____new ($$) {
74 my $self = bless \({}), shift;
75 $$self->{owner_document} = shift;
76 Scalar::Util::weaken ($$self->{owner_document});
77 return $self;
78 } # ____new
79
80 sub ___report_error ($$) {
81 $_[1]->throw;
82 } # ___report_error
83
84 sub AUTOLOAD {
85 my $method_name = our $AUTOLOAD;
86 $method_name =~ s/.*:://;
87 return if $method_name eq 'DESTROY';
88
89 if ({
90 ## Read-only attributes (trivial accessors)
91 owner_document => 1,
92 parent_node => 1,
93 manakai_read_only => 1,
94 }->{$method_name}) {
95 no strict 'refs';
96 eval qq{
97 sub $method_name (\$) {
98 return \${\$_[0]}->{$method_name};
99 }
100 };
101 goto &{ $AUTOLOAD };
102 } elsif (my $module_name = {
103 add_event_listener => 'Message::DOM::EventTargetNode',
104 add_event_listener_ns => 'Message::DOM::EventTargetNode',
105 dispatch_event => 'Message::DOM::EventTargetNode',
106 remove_event_listener => 'Message::DOM::EventTargetNode',
107 remove_event_listener_ns => 'Message::DOM::EventTargetNode',
108 }->{$method_name}) {
109 eval qq{ require $module_name } or die $@;
110 goto &{ $AUTOLOAD };
111 } else {
112 require Carp;
113 Carp::croak (qq<Can't locate method "$AUTOLOAD">);
114 }
115 } # AUTOLOAD
116
117 ## |Node| attributes
118
119 ## NOTE: Overridden by |Element|.
120 sub attributes () { undef }
121
122 sub base_uri ($) {
123 ## NOTE: Overridden by |Attr|, |CharacterData|, |Document|, |DocumentType|,
124 ## |Element|, |EntityReference|, and |ProcessingInstruction|.
125
126 local $Error::Depth = $Error::Depth + 1;
127 return $_[0]->owner_document->base_uri;
128 } # base_uri
129
130 sub child_nodes ($) {
131 ## NOTE: Overridden by |CharacterData|, |ElementTypeDefinition|,
132 ## |Notation|, and |ProcessingInstruction|.
133 require Message::DOM::NodeList;
134 return bless \\($_[0]), 'Message::DOM::NodeList::ChildNodeList';
135 } # child_nodes
136
137 sub manakai_expanded_uri ($) {
138 my $self = shift;
139 local $Error::Depth = $Error::Depth + 1;
140 my $ln = $self->local_name;
141 if (defined $ln) {
142 my $nsuri = $self->namespace_uri;
143 if (defined $nsuri) {
144 return $nsuri . $ln;
145 } else {
146 return $ln;
147 }
148 } else {
149 return undef;
150 }
151 } # manakai_expanded_uri
152
153 sub first_child ($) {
154 my $self = shift;
155 return $$self->{child_nodes} ? $$self->{child_nodes}->[0] : undef;
156 } # first_child
157
158 sub manakai_language ($;$) {
159 my $self = $_[0];
160 local $Error::Depth = $Error::Depth + 1;
161
162 if (@_ > 1) {
163 if ($self->node_type == 1) { # ELEMENT_NODE
164 if (defined $_[1]) {
165 if ($self->has_attribute_ns (undef, 'xml:lang')) {
166 $self->set_attribute_ns (undef, [undef, 'xml:lang'] => $_[1]);
167 # or exception
168 } else {
169 $self->set_attribute_ns
170 (q<http://www.w3.org/XML/1998/namespace>, 'xml:lang', $_[1]);
171 }
172 } else {
173 $self->remove_attribute_ns
174 (q<http://www.w3.org/XML/1998/namespace>, 'lang');
175 $self->remove_attribute_ns (undef, 'xml:lang');
176 }
177 }
178
179 return undef unless defined wantarray;
180 }
181
182 my $target = $self;
183 while (defined $target) {
184 if ($target->node_type == 1) { # ELEMENT_NODE
185 ## Step 1
186
187 ## Step 1.1
188 my $r = $target->get_attribute_ns
189 (q<http://www.w3.org/XML/1998/namespace>, 'lang');
190 return $r if defined $r;
191
192 ## Step 1.2
193 $r = $target->get_attribute_ns (undef, 'xml:lang');
194 return $r if defined $r;
195 }
196
197 ## Step 2
198 $target = $target->parent_node;
199 }
200
201 ## Step 3
202 my $od = $self->owner_document;
203 if (defined $od) {
204 return $od->manakai_language;
205 }
206
207 ## Step 4
208 ## TODO: from upper-level protocol, if $self isa Document
209
210 ## Step 5
211 return '';
212 } # manakai_language
213
214 ## TODO: document
215 sub manakai_html_language ($;$) {
216 my $self = $_[0];
217 local $Error::Depth = $Error::Depth + 1;
218
219 if (@_ > 1) {
220 if ($self->node_type == 1) { # ELEMENT_NODE
221 if (defined $_[1]) {
222 ## TODO: non HTML
223 if ($self->has_attribute_ns (undef, 'xml:lang')) {
224 $self->set_attribute_ns (undef, [undef, 'xml:lang'] => $_[1]);
225 # or exception
226 } else {
227 $self->set_attribute (lang => $_[1]);
228 }
229 } else {
230 ## TODO: html lang
231 $self->remove_attribute_ns
232 (q<http://www.w3.org/XML/1998/namespace>, 'lang');
233 $self->remove_attribute_ns (undef, 'xml:lang');
234 }
235 }
236
237 return undef unless defined wantarray;
238 }
239
240 my $target = $self;
241 while (defined $target) {
242 if ($target->node_type == 1) { # ELEMENT_NODE
243
244 ## TODO: non-html
245 my $r = $target->get_attribute ('lang');
246 return $r if defined $r;
247
248 ## Step 1
249
250 ## Step 1.1
251 $r = $target->get_attribute_ns
252 (q<http://www.w3.org/XML/1998/namespace>, 'lang');
253 return $r if defined $r;
254
255 ## Step 1.2
256 $r = $target->get_attribute_ns (undef, 'xml:lang');
257 return $r if defined $r;
258 }
259
260 ## Step 2
261 $target = $target->parent_node;
262 }
263
264 ## Step 3
265 my $od = $self->owner_document;
266 if (defined $od) {
267 return $od->manakai_language;
268 }
269
270 ## Step 4
271 ## TODO: from upper-level protocol, if $self isa Document
272
273 ## Step 5
274 return '';
275 } # manakai_html_language
276
277 sub last_child ($) {
278 my $self = shift;
279 return $$self->{child_nodes} && $$self->{child_nodes}->[0]
280 ? $$self->{child_nodes}->[-1] : undef;
281 } # last_child
282
283 sub local_name { undef }
284
285 sub manakai_local_name { undef }
286
287 sub namespace_uri { undef }
288
289 sub next_sibling ($) {
290 my $self = shift;
291 my $parent = $$self->{parent_node};
292 return undef unless defined $parent;
293 my $has_self;
294 for (@{$parent->child_nodes}) {
295 if ($_ eq $self) {
296 $has_self = 1;
297 } elsif ($has_self) {
298 return $_;
299 }
300 }
301 return undef;
302 } # next_sibling
303
304 ## NOTE: Overridden by subclasses.
305 sub node_name () { undef }
306
307 ## NOTE: Overridden by subclasses.
308 sub node_type () { }
309
310 ## NOTE: Overridden by |Attr|, |AttributeDefinition|,
311 ## |CharacterData|, and |ProcessingInstruction|.
312 sub node_value () { undef }
313
314 sub owner_document ($);
315
316 sub manakai_parent_element ($) {
317 my $self = shift;
318 my $parent = $$self->{parent_node};
319 while (defined $parent) {
320 if ($parent->node_type == ELEMENT_NODE) {
321 return $parent;
322 } else {
323 $parent = $$parent->{parent_node};
324 }
325 }
326 return undef;
327 } # manakai_parent_element
328
329 sub parent_node ($);
330
331 ## NOTE: Overridden by |Element| and |Attr|.
332 sub prefix ($;$) { undef }
333
334 sub previous_sibling ($) {
335 my $self = shift;
336 my $parent = $$self->{parent_node};
337 return undef unless defined $parent;
338 my $prev;
339 for (@{$parent->child_nodes}) {
340 if ($_ eq $self) {
341 return $prev;
342 } else {
343 $prev = $_;
344 }
345 }
346 return undef;
347 } # previous_sibling
348
349 sub manakai_read_only ($);
350
351 sub text_content ($;$) {
352 ## NOTE: For |Element|, |Attr|, |Entity|, |EntityReference|,
353 ## |DocumentFragment|, and |AttributeDefinition|. In addition,
354 ## |Document|'s |text_content| might call this attribute.
355
356 ## NOTE: Overridden by |Document|, |DocumentType|, |Notation|,
357 ## |CharacterData|, |ProcessingInstruction|, and |ElementTypeDefinition|.
358
359 my $self = $_[0];
360
361 if (@_ > 1) {
362 if (${$$self->{owner_document} or $self}->{strict_error_checking} and
363 $$self->{manakai_read_only}) {
364 report Message::DOM::DOMException
365 -object => $self,
366 -type => 'NO_MODIFICATION_ALLOWED_ERR',
367 -subtype => 'READ_ONLY_NODE_ERR';
368 }
369
370 local $Error::Depth = $Error::Depth + 1;
371 @{$self->child_nodes} = ();
372 if (defined $_[1] and length $_[1]) {
373 ## NOTE: |DocumentType| don't use this code.
374 my $text = ($$self->{owner_document} || $self)->create_text_node ($_[1]);
375 $self->append_child ($text);
376 }
377 }
378
379 if (defined wantarray) {
380 local $Error::Depth = $Error::Depth + 1;
381 my $r = '';
382 my @node = @{$self->child_nodes};
383 while (@node) {
384 my $child = shift @node;
385 my $child_nt = $child->node_type;
386 if ($child_nt == TEXT_NODE or $child_nt == CDATA_SECTION_NODE) {
387 $r .= $child->node_value unless $child->is_element_content_whitespace;
388 } elsif ($child_nt == COMMENT_NODE or
389 $child_nt == PROCESSING_INSTRUCTION_NODE or
390 $child_nt == DOCUMENT_TYPE_NODE) {
391 #
392 } else {
393 unshift @node, @{$child->child_nodes};
394 }
395 }
396 return $r;
397 }
398 } # text_content
399
400 ## |Node| methods
401
402 sub append_child ($$) {
403 ## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|.
404 ## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|,
405 ## |Notation|, |ProcessingInstruction| |ElementTypeDefinition|,
406 ## and |DocumentType| define their own implementations.
407 my $self = $_[0];
408
409 ## NOTE: Depends on $self->node_type:
410 my $self_od = $$self->{owner_document};
411
412 ## -- Node Type check
413 my @new_child;
414 my $new_child_parent;
415 if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) {
416 push @new_child, @{$_[1]->child_nodes};
417 $new_child_parent = $_[1];
418 } else {
419 @new_child = ($_[1]);
420 $new_child_parent = $_[1]->parent_node;
421 }
422
423 ## NOTE: Depends on $self->node_type:
424 if ($$self_od->{strict_error_checking}) {
425 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
426 if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) {
427 report Message::DOM::DOMException
428 -object => $self,
429 -type => 'WRONG_DOCUMENT_ERR',
430 -subtype => 'EXTERNAL_OBJECT_ERR';
431 }
432
433 if ($$self->{manakai_read_only} or
434 (@new_child and defined $new_child_parent and
435 $$new_child_parent->{manakai_read_only})) {
436 report Message::DOM::DOMException
437 -object => $self,
438 -type => 'NO_MODIFICATION_ALLOWED_ERR',
439 -subtype => 'READ_ONLY_NODE_ERR';
440 }
441
442 ## NOTE: |Document| has children order check here.
443
444 for my $cn (@new_child) {
445 unless ({
446 TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1,
447 ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1,
448 PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1,
449 }->{$cn->node_type}) {
450 report Message::DOM::DOMException
451 -object => $self,
452 -type => 'HIERARCHY_REQUEST_ERR',
453 -subtype => 'CHILD_NODE_TYPE_ERR';
454 }
455 }
456
457 my $anode = $self;
458 while (defined $anode) {
459 if ($anode eq $_[1]) {
460 report Message::DOM::DOMException
461 -object => $self,
462 -type => 'HIERARCHY_REQUEST_ERR',
463 -subtype => 'ANCESTOR_NODE_ERR';
464 }
465 $anode = $$anode->{parent_node};
466 }
467 }
468
469 ## NOTE: "Insert at" code only in insert_before and replace_child
470
471 ## -- Removes from parent
472 if ($new_child_parent) {
473 if (@new_child == 1) {
474 my $v = $$new_child_parent->{child_nodes};
475 RP: for my $i (0..$#$v) {
476 if ($v->[$i] eq $new_child[0]) {
477 splice @$v, $i, 1, ();
478 last RP;
479 }
480 } # RP
481 } else {
482 @{$$new_child_parent->{child_nodes}} = ();
483 }
484 }
485
486 ## -- Rewrite the |parentNode| properties
487 for my $nc (@new_child) {
488 $$nc->{parent_node} = $self;
489 Scalar::Util::weaken ($$nc->{parent_node});
490 }
491
492 ## NOTE: Depends on method:
493 push @{$$self->{child_nodes}}, @new_child;
494
495 ## NOTE: Setting |owner_document| in |Document|.
496
497 return $_[1];
498 } # apepnd_child
499
500 sub clone_node ($;$) {
501 my ($self, $deep) = @_;
502
503 ## TODO: Cloning operation
504 ## for ElementTypeDefinition, and AttributeDefinition nodes,
505 ## as well as new attributes introduced in DOM XML Document Type Definition
506 ## module.
507 ## TODO: if default attributes and attributedefinition are inconsistent
508
509 local $Error::Depth = $Error::Depth + 1;
510 my $od = $self->owner_document;
511 my $strict_check = $od->strict_error_checking;
512 $od->strict_error_checking (0);
513 my $cfg = $od->dom_config;
514 my $er_copy_asis
515 = $cfg->get_parameter
516 (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>);
517
518 my $r;
519 my @udh;
520 my @node = ([$self]);
521 while (@node) {
522 my ($node, $parent) = @{shift @node};
523 my $nt = $node->node_type;
524 my $clone;
525 if ($nt == ELEMENT_NODE) {
526 $clone = $od->create_element_ns
527 ($node->namespace_uri, [$node->prefix, $node->local_name]);
528 if ($parent) {
529 $parent->append_child ($clone);
530 } else {
531 $r = $clone;
532 }
533 my $attrs = $node->attributes;
534 my $attrsMax = @$attrs - 1;
535 for my $i (0..$attrsMax) {
536 my $attr = $attrs->[$i];
537 push @node, [$attr, $clone] if $attr->specified;
538 }
539 if ($deep) {
540 push @node, map {[$_, $clone]} @{$node->child_nodes};
541 }
542 } elsif ($nt == TEXT_NODE) {
543 $clone = $od->create_text_node ($node->data);
544 if ($parent) {
545 $parent->append_child ($clone);
546 } else {
547 $r = $clone;
548 }
549 $clone->is_element_content_whitespace (1)
550 if $node->is_element_content_whitespace;
551 } elsif ($nt == ATTRIBUTE_NODE) {
552 $clone = $od->create_attribute_ns
553 ($node->namespace_uri, [$node->prefix, $node->local_name]);
554 if ($parent) {
555 $parent->set_attribute_node_ns ($clone);
556 } else {
557 $r = $clone;
558 }
559 $clone->specified (1);
560 push @node, map {[$_, $clone]} @{$node->child_nodes};
561 } elsif ($nt == COMMENT_NODE) {
562 $clone = $od->create_comment ($node->data);
563 if ($parent) {
564 $parent->append_child ($clone);
565 } else {
566 $r = $clone;
567 }
568 } elsif ($nt == CDATA_SECTION_NODE) {
569 $clone = $od->create_cdata_section ($node->data);
570 if ($parent) {
571 $parent->append_child ($clone);
572 } else {
573 $r = $clone;
574 }
575 } elsif ($nt == PROCESSING_INSTRUCTION_NODE) {
576 $clone = $od->create_processing_instruction
577 ($node->target, $node->data);
578 if ($parent) {
579 $parent->append_child ($clone);
580 } else {
581 $r = $clone;
582 }
583 } elsif ($nt == ENTITY_REFERENCE_NODE) {
584 $clone = $od->create_entity_reference ($node->node_name);
585 if ($er_copy_asis) {
586 $clone->manakai_set_read_only (0);
587 $clone->text_content (0);
588 for (@{$node->child_nodes}) {
589 $clone->append_child ($_->clone_node (1));
590 }
591 $clone->manakai_expanded ($node->manakai_expanded);
592 $clone->manakai_set_read_only (1, 1);
593 } # copy asis
594 if ($parent) {
595 $parent->append_child ($clone);
596 } else {
597 $r = $clone;
598 }
599 } elsif ($nt == DOCUMENT_FRAGMENT_NODE) {
600 $clone = $od->create_document_fragment;
601 $r = $clone;
602 push @node, map {[$_, $clone]} @{$node->child_nodes};
603 } elsif ($nt == DOCUMENT_NODE) {
604 $od->strict_error_checking ($strict_check);
605 report Message::DOM::DOMException
606 -object => $self,
607 -type => 'NOT_SUPPORTED_ERR',
608 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
609 } elsif ($nt == DOCUMENT_TYPE_NODE) {
610 $od->strict_error_checking ($strict_check);
611 report Message::DOM::DOMException
612 -object => $self,
613 -type => 'NOT_SUPPORTED_ERR',
614 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
615 } elsif ($nt == ENTITY_NODE) {
616 $od->strict_error_checking ($strict_check);
617 report Message::DOM::DOMException
618 -object => $self,
619 -type => 'NOT_SUPPORTED_ERR',
620 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
621 } elsif ($nt == NOTATION_NODE) {
622 $od->strict_error_checking ($strict_check);
623 report Message::DOM::DOMException
624 -object => $self,
625 -type => 'NOT_SUPPORTED_ERR',
626 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
627 } else {
628 $od->strict_error_checking ($strict_check);
629 report Message::DOM::DOMException
630 -object => $self,
631 -type => 'NOT_SUPPORTED_ERR',
632 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
633 }
634
635 my $udhs = $$self->{user_data};
636 push @udh, [$node => $clone, $udhs] if $udhs and %$udhs;
637 } # @node
638 $od->strict_error_checking (1) if $strict_check;
639
640 ## Calling user data handlers if any
641 for my $sd (@udh) {
642 my $src = $sd->[0];
643 my $src_ud = $sd->[2];
644 for my $key (keys %{$src_ud}) {
645 my $dh = $src_ud->{$key}->[1];
646 if ($dh) { ## NODE_CLONED
647 $dh->handle (1, $key, $src_ud->{$key}->[0], $src, $sd->[1]);
648 ## ISSUE: |handler| method? CODE?
649 }
650 }
651 }
652
653 return $r;
654 } # clone_node
655
656 sub compare_document_position ($$) {
657 ## ISSUE: There are implementation specifics
658 ## (see what Gecko does if it implement this method...)
659
660 ## ISSUE: Maybe we should overload <=> or cmp
661
662 ## TODO: Too long method name! Too long constant names!
663 ## Too many thing to be done by a method!
664 ## Maybe we should import simpler method implemented by IE.
665
666 ## TODO: ElementTypeDefinition and AttributeDefinition
667
668 my @acontainer = ($_[0]);
669 my @bcontainer = ($_[1]);
670 F: {
671 A: while (1) {
672 if ($acontainer[-1] eq $bcontainer[-1]) {
673 last F;
674 } else {
675 my $ap;
676 my $atype = $acontainer[-1]->node_type;
677 if ($atype == ATTRIBUTE_NODE) {
678 $ap = $acontainer[-1]->owner_element;
679 } elsif ($atype == ENTITY_NODE or $atype == NOTATION_NODE or
680 $atype == ELEMENT_TYPE_DEFINITION_NODE) {
681 $ap = $acontainer[-1]->owner_document_type_definition;
682 } elsif ($atype == ATTRIBUTE_DEFINITION_NODE) {
683 $ap = $acontainer[-1]->owner_element_type_definition;
684 } else {
685 $ap = $acontainer[-1]->parent_node;
686 }
687 if (defined $ap) {
688 push @acontainer, $ap;
689 } else {
690 last A;
691 }
692 }
693 } # A
694
695 B: while (1) {
696 if ($acontainer[-1] eq $bcontainer[-1]) {
697 last F;
698 } else {
699 my $bp;
700 my $btype = $bcontainer[-1]->node_type;
701 if ($btype == ATTRIBUTE_NODE) {
702 $bp = $bcontainer[-1]->owner_element;
703 } elsif ($btype == ENTITY_NODE or $btype == NOTATION_NODE or
704 $btype == ELEMENT_TYPE_DEFINITION_NODE) {
705 $bp = $bcontainer[-1]->owner_document_type_definition;
706 } elsif ($btype == ATTRIBUTE_DEFINITION_NODE) {
707 $bp = $bcontainer[-1]->owner_element_type_definition;
708 } else {
709 $bp = $bcontainer[-1]->parent_node;
710 }
711 if (defined $bp) {
712 push @bcontainer, $bp;
713 } else {
714 last B;
715 }
716 }
717 } # B
718
719 ## Disconnected
720 if ($bcontainer[-1]->isa ('Message::IF::Node')) {
721 ## ISSUE: Document this in manakai's DOM Perl Binding?
722 return DOCUMENT_POSITION_DISCONNECTED
723 | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
724 | ((${$acontainer[-1]} cmp ${$bcontainer[-1]}) > 0
725 ? DOCUMENT_POSITION_FOLLOWING
726 : DOCUMENT_POSITION_PRECEDING);
727 } else {
728 ## TODO: Is there test cases for this?
729 return DOCUMENT_POSITION_DISCONNECTED
730 | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
731 | DOCUMENT_POSITION_FOLLOWING;
732 }
733 } # F
734
735 ## Common container found
736 if (@acontainer >= 2) {
737 if (@bcontainer >= 2) {
738 my $acnt = $acontainer[-2]->node_type;
739 my $bcnt = $bcontainer[-2]->node_type;
740 if ($acnt == ATTRIBUTE_NODE or
741 $acnt == NOTATION_NODE or
742 $acnt == ELEMENT_TYPE_DEFINITION_NODE or
743 $acnt == ATTRIBUTE_DEFINITION_NODE) {
744 if ($acnt == $bcnt) {
745 return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
746 | (($acontainer[-2]->node_name cmp
747 $bcontainer[-2]->node_name) > 0
748 ? DOCUMENT_POSITION_FOLLOWING
749 : DOCUMENT_POSITION_PRECEDING);
750 } elsif ($bcnt == ATTRIBUTE_NODE or
751 $bcnt == NOTATION_NODE or
752 $bcnt == ELEMENT_TYPE_DEFINITION_NODE or
753 $bcnt == ATTRIBUTE_DEFINITION_NODE) {
754 return (($acnt < $bcnt)
755 ? DOCUMENT_POSITION_FOLLOWING
756 : DOCUMENT_POSITION_PRECEDING);
757 } else {
758 ## A: Non-child and B: child
759 return DOCUMENT_POSITION_FOLLOWING;
760 }
761 } elsif ($bcnt == ATTRIBUTE_NODE or
762 $bcnt == NOTATION_NODE or
763 $bcnt == ELEMENT_TYPE_DEFINITION_NODE or
764 $bcnt == ATTRIBUTE_DEFINITION_NODE) {
765 ## A: Child and B: non-child
766 return DOCUMENT_POSITION_PRECEDING;
767 } else {
768 ## A and B are both children
769 for my $cn (@{$acontainer[-1]->child_nodes}) {
770 if ($cn eq $acontainer[-2]) {
771 return DOCUMENT_POSITION_FOLLOWING;
772 } elsif ($cn eq $bcontainer[-2]) {
773 return DOCUMENT_POSITION_PRECEDING;
774 }
775 }
776 die "compare_document_position: Something wrong (1)";
777 }
778 } else {
779 ## B contains A
780 return DOCUMENT_POSITION_CONTAINS
781 | DOCUMENT_POSITION_PRECEDING;
782 }
783 } else {
784 if (@bcontainer >= 2) {
785 ## A contains B
786 return DOCUMENT_POSITION_CONTAINED_BY
787 | DOCUMENT_POSITION_FOLLOWING;
788 } else {
789 ## A eq B
790 return 0;
791 }
792 }
793 die "compare_document_position: Something wrong (2)";
794 } # compare_document_position
795
796 sub get_feature ($$;$) {
797 my $feature = lc $_[1]; ## TODO: |lc|?
798 $feature =~ s/^\+//;
799 my $version = defined $_[2] ? $_[2] : '';
800 if ($Message::DOM::DOMImplementation::HasFeature->{$feature}->{$version}) {
801 return $_[0];
802 } else {
803 return undef;
804 }
805 } # get_feature
806
807 sub get_user_data ($$) {
808 if (${$_[0]}->{user_data}->{$_[1]}) {
809 return ${$_[0]}->{user_data}->{$_[1]}->[0];
810 } else {
811 return undef;
812 }
813 } # get_user_data
814
815 sub has_attributes ($) {
816 for (values %{${$_[0]}->{attributes} or {}}) {
817 return 1 if keys %$_;
818 }
819 return 0;
820 } # has_attributes
821
822 sub has_child_nodes ($) {
823 return (@{${$_[0]}->{child_nodes} or []} > 0);
824 } # has_child_nodes
825
826 sub insert_before ($$) {
827 ## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|.
828 ## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|,
829 ## |Notation|, |ProcessingInstruction|, |ElementTypeDefinition|,
830 ## and |DocumentType| define their own implementations.
831 my $self = $_[0];
832
833 ## NOTE: Depends on $self->node_type:
834 my $self_od = $$self->{owner_document};
835
836 ## -- Node Type check
837 my @new_child;
838 my $new_child_parent;
839 if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) {
840 push @new_child, @{$_[1]->child_nodes};
841 $new_child_parent = $_[1];
842 } else {
843 @new_child = ($_[1]);
844 $new_child_parent = $_[1]->parent_node;
845 }
846
847 ## NOTE: Depends on $self->node_type:
848 if ($$self_od->{strict_error_checking}) {
849 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
850 if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) {
851 report Message::DOM::DOMException
852 -object => $self,
853 -type => 'WRONG_DOCUMENT_ERR',
854 -subtype => 'EXTERNAL_OBJECT_ERR';
855 }
856
857 if ($$self->{manakai_read_only} or
858 (@new_child and defined $new_child_parent and
859 $$new_child_parent->{manakai_read_only})) {
860 report Message::DOM::DOMException
861 -object => $self,
862 -type => 'NO_MODIFICATION_ALLOWED_ERR',
863 -subtype => 'READ_ONLY_NODE_ERR';
864 }
865
866 ## NOTE: |Document| has children order check here.
867
868 for my $cn (@new_child) {
869 unless ({
870 TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1,
871 ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1,
872 PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1,
873 }->{$cn->node_type}) {
874 report Message::DOM::DOMException
875 -object => $self,
876 -type => 'HIERARCHY_REQUEST_ERR',
877 -subtype => 'CHILD_NODE_TYPE_ERR';
878 }
879 }
880
881 my $anode = $self;
882 while (defined $anode) {
883 if ($anode eq $_[1]) {
884 report Message::DOM::DOMException
885 -object => $self,
886 -type => 'HIERARCHY_REQUEST_ERR',
887 -subtype => 'ANCESTOR_NODE_ERR';
888 }
889 $anode = $$anode->{parent_node};
890 }
891 }
892
893 ## -- Insert at... ## NOTE: Only in insert_before and replace_child
894 my $index = -1; # last
895 if (defined $_[2]) {
896 ## error if $_[1] eq $_[2];
897
898 my $cns = $self->child_nodes;
899 my $cnsl = @$cns;
900 C: {
901 $index = 0;
902 for my $i (0..($cnsl-1)) {
903 my $cn = $cns->[$i];
904 if ($cn eq $_[2]) {
905 $index += $i;
906 last C;
907 } elsif ($cn eq $_[1]) {
908 $index = -1; # offset
909 }
910 }
911
912 report Message::DOM::DOMException
913 -object => $self,
914 -type => 'NOT_FOUND_ERR',
915 -subtype => 'NOT_CHILD_ERR';
916 } # C
917 }
918 ## NOTE: "else" only in replace_child
919
920 ## -- Removes from parent
921 if ($new_child_parent) {
922 if (@new_child == 1) {
923 my $v = $$new_child_parent->{child_nodes};
924 RP: for my $i (0..$#$v) {
925 if ($v->[$i] eq $new_child[0]) {
926 splice @$v, $i, 1, ();
927 last RP;
928 }
929 } # RP
930 } else {
931 @{$$new_child_parent->{child_nodes}} = ();
932 }
933 }
934
935 ## -- Rewrite the |parentNode| properties
936 for my $nc (@new_child) {
937 $$nc->{parent_node} = $self;
938 Scalar::Util::weaken ($$nc->{parent_node});
939 }
940
941 ## NOTE: Depends on method:
942 if ($index == -1) {
943 push @{$$self->{child_nodes}}, @new_child;
944 } else {
945 splice @{$$self->{child_nodes}}, $index, 0, @new_child;
946 }
947
948 ## NOTE: Setting |owner_document| in |Document|.
949
950 return $_[1];
951 } # insert_before
952
953 sub is_equal_node ($$) {
954 local $Error::Depth = $Error::Depth + 1;
955
956 return 0 unless UNIVERSAL::isa ($_[1], 'Message::IF::Node');
957
958 my $nt = $_[0]->node_type;
959 return 0 unless $nt == $_[1]->node_type;
960
961 my @str_attr = qw/node_name local_name namespace_uri
962 prefix node_value/;
963 push @str_attr, qw/public_id system_id internal_subset/
964 if $nt == DOCUMENT_TYPE_NODE;
965 for my $attr_name (@str_attr) {
966 my $v1 = $_[0]->can ($attr_name) ? $_[0]->$attr_name : undef;
967 my $v2 = $_[1]->can ($attr_name) ? $_[1]->$attr_name : undef;
968 if (defined $v1 and defined $v2) {
969 return 0 unless ''.$v1 eq ''.$v2;
970 } elsif (defined $v1 or defined $v2) {
971 return 0;
972 }
973 }
974
975 my @num_eq_attr = qw/child_nodes attributes/;
976 push @num_eq_attr, qw/entities notations element_types/
977 if $nt == DOCUMENT_TYPE_NODE;
978 push @num_eq_attr, qw/attribute_definitions/
979 if $nt == ELEMENT_TYPE_DEFINITION_NODE;
980 push @num_eq_attr, qw/declared_type default_type allowed_tokens/
981 if $nt == ATTRIBUTE_DEFINITION_NODE;
982 for my $attr_name (@num_eq_attr) {
983 my $v1 = $_[0]->can ($attr_name) ? $_[0]->$attr_name : undef;
984 my $v2 = $_[1]->can ($attr_name) ? $_[1]->$attr_name : undef;
985 if (defined $v1 and defined $v2) {
986 return 0 unless $v1 == $v2;
987 } elsif (defined $v1 or defined $v2) {
988 return 0;
989 }
990 }
991
992 return 1;
993 } # is_equal_node
994
995 sub is_same_node ($$) { $_[0] eq $_[1] }
996
997 sub is_supported ($$;$) {
998 my $feature = lc $_[1]; ## TODO: |lc|?
999 my $plus = ($feature =~ s/^\+//);
1000 my $version = defined $_[2] ? $_[2] : '';
1001 return $Message::DOM::DOMImplementation::HasFeature->{$feature}->{$version};
1002 } # is_supported;
1003
1004 sub manakai_append_text ($$) {
1005 ## NOTE: For |Element|, |Attr|, |Entity|, |EntityReference|,
1006 ## |DocumentFragment|, and |AttributeDefinition|. In addition,
1007 ## |Document|'s |text_content| might call this attribute.
1008
1009 ## NOTE: Overridden by |Document|, |DocumentType|, |CharacterData|,
1010 ## |ElementTypeDefinition|, |Notation|, and |ProcessingInstruction|.
1011
1012 my $self = $_[0];
1013 local $Error::Depth = $Error::Depth + 1;
1014 if (@{$$self->{child_nodes}} and
1015 $$self->{child_nodes}->[-1]->node_type == TEXT_NODE) {
1016 $$self->{child_nodes}->[-1]->manakai_append_text ($_[1]);
1017 } else {
1018 my $text = ($$self->{owner_document} or $self)->create_text_node ($_[1]);
1019 $self->append_child ($text);
1020 }
1021 } # manakai_append_text
1022
1023 sub is_default_namespace ($$) {
1024 local $Error::Depth = $Error::Depth + 1;
1025 my $namespace_uri = defined $_[1] ? $_[1] : '';
1026 my $nt = $_[0]->node_type;
1027 if ($nt == ELEMENT_NODE) {
1028 my $el = $_[0];
1029 EL: {
1030 unless (defined $el->prefix) {
1031 my $elns = $el->namespace_uri;
1032 if ($namespace_uri ne '' and defined $elns) {
1033 return $namespace_uri eq $elns;
1034 } else {
1035 return not ($namespace_uri eq '' or defined $elns);
1036 }
1037 }
1038 my $xmlns = $el->get_attribute_ns
1039 ('http://www.w3.org/2000/xmlns/', 'xmlns');
1040 if (defined $xmlns) {
1041 if ($namespace_uri ne '') {
1042 return ($namespace_uri eq $xmlns);
1043 } else {
1044 return ($xmlns eq '');
1045 }
1046 }
1047 $el = $el->manakai_parent_element;
1048 redo EL if defined $el;
1049 return 0;
1050 } # EL;
1051 } else {
1052 my $el = $nt == DOCUMENT_NODE
1053 ? $_[0]->document_element
1054 : $nt == ATTRIBUTE_NODE
1055 ? $_[0]->owner_element
1056 : $_[0]->manakai_parent_element;
1057 if (defined $el) {
1058 return $el->is_default_namespace ($_[1]);
1059 } else {
1060 return 0;
1061 }
1062 }
1063 } # is_default_namespace
1064
1065 sub lookup_namespace_uri ($$) {
1066 my ($self, $prefix) = @_;
1067 $prefix = undef if defined $prefix and $prefix eq '';
1068 ## NOTE: Implementation dependent.
1069 ## TODO: Check what Gecko does.
1070 local $Error::Depth = $Error::Depth + 1;
1071 my $nt = $self->node_type;
1072 if ($nt == ELEMENT_NODE) {
1073 my $el = $self;
1074 EL: {
1075 my $elns = $el->namespace_uri;
1076 if (defined $elns) {
1077 my $elpfx = $el->prefix;
1078 if ((not defined $prefix and not defined $elpfx) or
1079 (defined $prefix and defined $elpfx and $prefix eq $elpfx)) {
1080 return $elns;
1081 }
1082 }
1083 AT: for my $attr (@{$el->attributes}) {
1084 my $attrns = $attr->namespace_uri;
1085 next AT if not defined $attrns or
1086 $attrns ne 'http://www.w3.org/2000/xmlns/';
1087 my $attrpfx = $attr->prefix;
1088 if (not defined $prefix) {
1089 my $attrln = $attr->local_name;
1090 if ($attrln eq 'xmlns') {
1091 my $attrval = $attr->value;
1092 return length $attrval ? $attrval : undef;
1093 }
1094 } elsif (defined $prefix and
1095 defined $attrpfx and $attrpfx eq 'xmlns') {
1096 my $attrln = $attr->local_name;
1097 if ($attrln eq $prefix) {
1098 my $attrval = $attr->value;
1099 return length $attrval ? $attrval : undef;
1100 }
1101 }
1102 } # AT
1103 $el = $el->manakai_parent_element;
1104 redo EL if defined $el;
1105 return undef;
1106 } # EL;
1107 } else {
1108 my $el = $nt == DOCUMENT_NODE
1109 ? $self->document_element
1110 : $nt == ATTRIBUTE_NODE
1111 ? $self->owner_element
1112 : $self->manakai_parent_element;
1113 if (defined $el) {
1114 return $el->lookup_namespace_uri ($prefix);
1115 } else {
1116 return undef;
1117 }
1118 }
1119 } # lookup_namespace_uri
1120
1121 sub lookup_prefix ($$) {
1122 my $namespace_uri = defined $_[1] ? $_[1] : '';
1123 if ($namespace_uri eq '') {
1124 return undef;
1125 }
1126
1127 local $Error::Depth = $Error::Depth + 1;
1128 my $nt = $_[0]->node_type;
1129 if ($nt == ELEMENT_NODE) {
1130 my $el = $_[0];
1131 EL: {
1132 my $elns = $el->namespace_uri;
1133 if (defined $elns and $elns eq $namespace_uri) {
1134 my $elpfx = $el->prefix;
1135 if (defined $elpfx) {
1136 my $oeluri = $_[0]->lookup_namespace_uri ($elpfx);
1137 if (defined $oeluri and $oeluri eq $namespace_uri) {
1138 return $elpfx;
1139 }
1140 }
1141 }
1142 AT: for my $attr (@{$el->attributes}) {
1143 my $attrpfx = $attr->prefix;
1144 next AT if not defined $attrpfx or $attrpfx ne 'xmlns';
1145 my $attrns = $attr->namespace_uri;
1146 next AT if not defined $attrns or
1147 $attrns ne 'http://www.w3.org/2000/xmlns/';
1148 next AT unless $attr->value eq $namespace_uri;
1149 my $attrln = $attr->local_name;
1150 my $oeluri = $el->lookup_namespace_uri ($attrln);
1151 next AT unless defined $oeluri;
1152 if ($oeluri eq $namespace_uri) {
1153 return $attrln;
1154 }
1155 }
1156 $el = $el->manakai_parent_element;
1157 redo EL if defined $el;
1158 return undef;
1159 } # EL
1160 } else {
1161 my $el = $nt == DOCUMENT_NODE
1162 ? $_[0]->document_element
1163 : $nt == ATTRIBUTE_NODE
1164 ? $_[0]->owner_element
1165 : $_[0]->manakai_parent_element;
1166 if (defined $el) {
1167 return $el->lookup_prefix ($_[1]);
1168 } else {
1169 return undef;
1170 }
1171 }
1172 } # lookup_prefix
1173
1174 sub normalize ($) {
1175 my $self = shift;
1176 my $ptext;
1177 local $Error::Depth = $Error::Depth + 1;
1178
1179 ## Children
1180 my @remove;
1181 for my $cn (@{$self->child_nodes}) {
1182 if ($cn->node_type == TEXT_NODE) {
1183 my $nv = $cn->node_value;
1184 if (length $nv) {
1185 if (defined $ptext) {
1186 $ptext->manakai_append_text ($nv);
1187 $ptext->is_element_content_whitespace (1)
1188 if $cn->is_element_content_whitespace and
1189 $ptext->is_element_content_whitespace;
1190 push @remove, $cn;
1191 } else {
1192 $ptext = $cn;
1193 }
1194 } else {
1195 push @remove, $cn;
1196 }
1197 } else {
1198 $cn->normalize;
1199 undef $ptext;
1200 }
1201 }
1202 $self->remove_child ($_) for @remove;
1203
1204 my $nt = $self->node_type;
1205 if ($nt == ELEMENT_NODE) {
1206 ## Attributes
1207 $_->normalize for @{$self->attributes};
1208 } elsif ($nt == DOCUMENT_TYPE_NODE) {
1209 ## Element type definitions
1210 $_->normalize for @{$self->element_types};
1211 ## General entities
1212 $_->normalize for @{$self->general_entities};
1213 } elsif ($nt == ELEMENT_TYPE_DEFINITION_NODE) {
1214 ## Attribute definitions
1215 $_->normalize for @{$self->attribute_definitions};
1216 }
1217 ## TODO: normalize-characters
1218
1219 ## TODO: In this implementation, if a modification raises a
1220 ## |NO_MODIFICATION_ALLOWED_ERR|, then any modification before it
1221 ## is not reverted.
1222 } # normalize
1223
1224 sub remove_child ($$) {
1225 my ($self, $old_child) = @_;
1226
1227 if ($$self->{manakai_read_only} and
1228 ${$$self->{owner_document} or $self}->{strict_error_checking}) {
1229 report Message::DOM::DOMException
1230 -object => $self,
1231 -type => 'NO_MODIFICATION_ALLOWED_ERR',
1232 -subtype => 'READ_ONLY_NODE_ERR';
1233 }
1234
1235 my $parent_list = $$self->{child_nodes} || [];
1236 for (0..$#$parent_list) {
1237 if ($parent_list->[$_] eq $old_child) {
1238 splice @$parent_list, $_, 1, ();
1239 delete $$old_child->{parent_node};
1240 return $old_child;
1241 }
1242 }
1243
1244 report Message::DOM::DOMException
1245 -object => $self,
1246 -type => 'NOT_FOUND_ERR',
1247 -subtype => 'NOT_CHILD_ERR';
1248 } # remove_child
1249
1250 sub replace_child ($$) {
1251 ## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|.
1252 ## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|,
1253 ## |Notation|, |ProcessingInstruction|, |ElementTypeDefinition|,
1254 ## and |DocumentType| define their own implementations.
1255 my $self = $_[0];
1256
1257 ## NOTE: Depends on $self->node_type:
1258 my $self_od = $$self->{owner_document};
1259
1260 ## -- Node Type check
1261 my @new_child;
1262 my $new_child_parent;
1263 if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) {
1264 push @new_child, @{$_[1]->child_nodes};
1265 $new_child_parent = $_[1];
1266 } else {
1267 @new_child = ($_[1]);
1268 $new_child_parent = $_[1]->parent_node;
1269 }
1270
1271 ## NOTE: Depends on $self->node_type:
1272 if ($$self_od->{strict_error_checking}) {
1273 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
1274 if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) {
1275 report Message::DOM::DOMException
1276 -object => $self,
1277 -type => 'WRONG_DOCUMENT_ERR',
1278 -subtype => 'EXTERNAL_OBJECT_ERR';
1279 }
1280
1281 if ($$self->{manakai_read_only} or
1282 (@new_child and defined $new_child_parent and
1283 $$new_child_parent->{manakai_read_only})) {
1284 report Message::DOM::DOMException
1285 -object => $self,
1286 -type => 'NO_MODIFICATION_ALLOWED_ERR',
1287 -subtype => 'READ_ONLY_NODE_ERR';
1288 }
1289
1290 ## NOTE: |Document| has children order check here.
1291
1292 for my $cn (@new_child) {
1293 unless ({
1294 TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1,
1295 ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1,
1296 PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1,
1297 }->{$cn->node_type}) {
1298 report Message::DOM::DOMException
1299 -object => $self,
1300 -type => 'HIERARCHY_REQUEST_ERR',
1301 -subtype => 'CHILD_NODE_TYPE_ERR';
1302 }
1303 }
1304
1305 my $anode = $self;
1306 while (defined $anode) {
1307 if ($anode eq $_[1]) {
1308 report Message::DOM::DOMException
1309 -object => $self,
1310 -type => 'HIERARCHY_REQUEST_ERR',
1311 -subtype => 'ANCESTOR_NODE_ERR';
1312 }
1313 $anode = $$anode->{parent_node};
1314 }
1315 }
1316
1317 ## -- Insert at... ## NOTE: Only in insertBefore and replaceChild
1318 my $index = -1; # last
1319 if (defined $_[2]) {
1320 ## error if $_[1] eq $_[2];
1321
1322 my $cns = $self->child_nodes;
1323 my $cnsl = @$cns;
1324 C: {
1325 $index = 0;
1326 for my $i (0..($cnsl-1)) {
1327 my $cn = $cns->[$i];
1328 if ($cn eq $_[2]) {
1329 $index += $i;
1330 last C;
1331 } elsif ($cn eq $_[1]) {
1332 $index = -1; # offset
1333 }
1334 }
1335
1336 report Message::DOM::DOMException
1337 -object => $self,
1338 -type => 'NOT_FOUND_ERR',
1339 -subtype => 'NOT_CHILD_ERR';
1340 } # C
1341 } else {
1342 ## NOTE: Only in replaceChild
1343 report Message::DOM::DOMException
1344 -object => $self,
1345 -type => 'NOT_FOUND_ERR',
1346 -subtype => 'NOT_CHILD_ERR';
1347 }
1348
1349 ## -- Removes from parent
1350 if ($new_child_parent) {
1351 if (@new_child == 1) {
1352 my $v = $$new_child_parent->{child_nodes};
1353 RP: for my $i (0..$#$v) {
1354 if ($v->[$i] eq $new_child[0]) {
1355 splice @$v, $i, 1, ();
1356 last RP;
1357 }
1358 } # RP
1359 } else {
1360 @{$$new_child_parent->{child_nodes}} = ();
1361 }
1362 }
1363
1364 ## -- Rewrite the |parentNode| properties
1365 for my $nc (@new_child) {
1366 $$nc->{parent_node} = $self;
1367 Scalar::Util::weaken ($$nc->{parent_node});
1368 }
1369
1370 ## NOTE: Depends on method:
1371 splice @{$$self->{child_nodes}}, $index, 1, @new_child;
1372 delete ${$_[2]}->{parent_node};
1373
1374 ## NOTE: Setting |owner_document| in |Document|.
1375
1376 return $_[2];
1377 } # replace_child
1378
1379 sub manakai_set_read_only ($;$$) {
1380 my $value = 1 if $_[1];
1381 if ($_[2]) {
1382 my @target = ($_[0]);
1383 while (@target) {
1384 my $target = shift @target;
1385 if ($value) {
1386 $$target->{manakai_read_only} = 1;
1387 } else {
1388 delete $$target->{manakai_read_only};
1389 }
1390 push @target, @{$target->child_nodes};
1391
1392 my $nt = $target->node_type;
1393 if ($nt == ELEMENT_NODE) {
1394 push @target, @{$target->attributes};
1395 } elsif ($nt == ELEMENT_TYPE_DEFINITION_NODE) {
1396 push @target, @{$target->attribute_definitions};
1397 } elsif ($nt == DOCUMENT_TYPE_NODE) {
1398 push @target, @{$target->element_types};
1399 push @target, @{$target->general_entities};
1400 push @target, @{$target->notations};
1401 }
1402 }
1403 } else { # not deep
1404 if ($value) {
1405 ${$_[0]}->{manakai_read_only} = 1;
1406 } else {
1407 delete ${$_[0]}->{manakai_read_only};
1408 }
1409 }
1410 } # manakai_set_read_only
1411
1412 # {NOTE:: Perl application developers are advised to be careful
1413 # to include direct or indirect references to the node
1414 # itself as user data or in user data handlers.
1415 # They would result in memory leak problems unless
1416 # the circular references are removed later.
1417 #
1418 # It would be a good practive to eusure that every user data
1419 # registered to a node is later unregistered by setting
1420 # <DOM::null> as a data for the same key.
1421 #
1422 sub set_user_data ($$;$$) {
1423 my ($self, $key, $data, $handler) = @_;
1424
1425 my $v = ($$self->{user_data} ||= {});
1426 my $r = $v->{$key}->[0];
1427
1428 if (defined $data) {
1429 $v->{$key} = [$data, $handler];
1430
1431 if (defined $handler) {
1432 eval q{
1433 no warnings;
1434 sub DESTROY {
1435 my $uds = ${$_[0]}->{user_data};
1436 for my $key (keys %$uds) {
1437 if (defined $uds->{$key}->[1]) {
1438 local $Error::Depth = $Error::Depth + 1;
1439 $uds->{$key}->[1]->(3, $key, $uds->{$key}->[0]); # NODE_DELETED
1440 }
1441 }
1442 }
1443 };
1444 }
1445 } else {
1446 delete $v->{$key};
1447 }
1448 return $r;
1449 } # set_user_data
1450
1451 package Message::IF::Node;
1452 package Message::IF::NSResolver;
1453
1454 =head1 LICENSE
1455
1456 Copyright 2007 Wakaba <w@suika.fam.cx>
1457
1458 This program is free software; you can redistribute it and/or
1459 modify it under the same terms as Perl itself.
1460
1461 =cut
1462
1463 1;
1464 ## $Date: 2007/12/22 06:29:32 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24