/[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.17 - (hide annotations) (download)
Mon Oct 8 07:17:18 2007 UTC (17 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.16: +11 -2 lines
++ manakai/lib/Message/DOM/ChangeLog	8 Oct 2007 07:16:56 -0000
2007-10-08  Wakaba  <wakaba@suika.fam.cx>

	* Event.pm, EventTarget.pm, EventTargetNode.pm,
	EventException.pm: Implemented (but not tested!).

	* DOMException.pm (MALFORMED_EVENT_TYPE_ERR,
	EVENT_INTERFACE_NOT_SUPPORTED_ERR, EXTERNAL_EVENT_ERR): New
	error subtypes.

	* DOMImplementation.pm ($HasFeature): The |Event| feature,
	version |3.0|, is added.

	* Document.pm, Node.pm: Event attributes and
	methods are implemented.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24