/[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.16 - (show annotations) (download)
Mon Sep 24 10:16:14 2007 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
CVS Tags: manakai-release-0-4-0
Changes since 1.15: +5 -3 lines
++ manakai/lib/Message/DOM/ChangeLog	24 Sep 2007 10:15:28 -0000
2007-09-24  Wakaba  <wakaba@suika.fam.cx>

	* DOMException.pm (SYNTAX_ERR): New subtype is defined.
	(UNDECLARED_PREFIX_ERR): New subtype is defined.

	* Document.pm (Document): Implements the |DocumentSelector|
	interface.

	* Element.pm (Element): Implements the |ElementSelector|
	interface.

	* Node.pm (Node): Implements the |NSResolver| interface.

	* SelectorsAPI.pm: Now (hopefully) conform to the Selectors
	API Editor's Draft (only |query_selector_all| on |Document|,
	with limited selectors syntax support, though).

++ manakai/t/ChangeLog	24 Sep 2007 10:16:05 -0000
2007-09-24  Wakaba  <wakaba@suika.fam.cx>

	* selectors-test-1.dat: New tests for pseudo-elements
	are added.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24