/[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 - (hide 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 wakaba 1.1 package Message::DOM::Node;
2     use strict;
3 wakaba 1.19 our $VERSION=do{my @r=(q$Revision: 1.18 $=~/\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.19 ## 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 wakaba 1.3 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 wakaba 1.6 sub local_name { undef }
284 wakaba 1.3
285 wakaba 1.6 sub manakai_local_name { undef }
286    
287     sub namespace_uri { undef }
288 wakaba 1.3
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 wakaba 1.2
304 wakaba 1.6 ## NOTE: Overridden by subclasses.
305     sub node_name () { undef }
306 wakaba 1.2
307 wakaba 1.6 ## NOTE: Overridden by subclasses.
308     sub node_type () { }
309 wakaba 1.2
310 wakaba 1.6 ## NOTE: Overridden by |Attr|, |AttributeDefinition|,
311     ## |CharacterData|, and |ProcessingInstruction|.
312     sub node_value () { undef }
313 wakaba 1.3
314     sub owner_document ($);
315    
316 wakaba 1.8 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 wakaba 1.3 sub parent_node ($);
330    
331 wakaba 1.6 ## NOTE: Overridden by |Element| and |Attr|.
332 wakaba 1.3 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 wakaba 1.4 sub manakai_read_only ($);
350    
351 wakaba 1.3 sub text_content ($;$) {
352 wakaba 1.6 ## 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 wakaba 1.7 if (${$$self->{owner_document} or $self}->{strict_error_checking} and
363     $$self->{manakai_read_only}) {
364 wakaba 1.6 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 wakaba 1.3 } # text_content
399    
400 wakaba 1.7 ## |Node| methods
401    
402 wakaba 1.12 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 wakaba 1.8 sub clone_node ($;$) {
501     my ($self, $deep) = @_;
502    
503 wakaba 1.18 ## TODO: Cloning operation
504 wakaba 1.8 ## for ElementTypeDefinition, and AttributeDefinition nodes,
505     ## as well as new attributes introduced in DOM XML Document Type Definition
506     ## module.
507 wakaba 1.18 ## TODO: if default attributes and attributedefinition are inconsistent
508 wakaba 1.8
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 wakaba 1.10 my $er_copy_asis
515     = $cfg->get_parameter
516     (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>);
517 wakaba 1.8
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 wakaba 1.10 $clone->is_element_content_whitespace (1)
550     if $node->is_element_content_whitespace;
551 wakaba 1.8 } 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 wakaba 1.18 ## TODO: ElementTypeDefinition and AttributeDefinition
667 wakaba 1.8
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 wakaba 1.11 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 wakaba 1.8 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 wakaba 1.12 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 wakaba 1.11 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 wakaba 1.4
961 wakaba 1.11 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 wakaba 1.1 } # is_equal_node
994    
995 wakaba 1.11 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 wakaba 1.7 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 wakaba 1.8 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 wakaba 1.1 sub remove_child ($$) {
1225     my ($self, $old_child) = @_;
1226 wakaba 1.12
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 wakaba 1.1 for (0..$#$parent_list) {
1237     if ($parent_list->[$_] eq $old_child) {
1238 wakaba 1.12 splice @$parent_list, $_, 1, ();
1239     delete $$old_child->{parent_node};
1240     return $old_child;
1241 wakaba 1.1 }
1242     }
1243 wakaba 1.12
1244     report Message::DOM::DOMException
1245     -object => $self,
1246     -type => 'NOT_FOUND_ERR',
1247     -subtype => 'NOT_CHILD_ERR';
1248 wakaba 1.1 } # remove_child
1249    
1250 wakaba 1.12 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 wakaba 1.4 sub manakai_set_read_only ($;$$) {
1380 wakaba 1.7 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 wakaba 1.4 } # manakai_set_read_only
1411    
1412 wakaba 1.11 # {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 wakaba 1.18 sub set_user_data ($$;$$) {
1423 wakaba 1.9 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 wakaba 1.11 eval q{
1433 wakaba 1.12 no warnings;
1434 wakaba 1.11 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 wakaba 1.9 }
1442     }
1443     };
1444     }
1445     } else {
1446     delete $v->{$key};
1447     }
1448     return $r;
1449     } # set_user_data
1450    
1451 wakaba 1.4 package Message::IF::Node;
1452 wakaba 1.16 package Message::IF::NSResolver;
1453 wakaba 1.4
1454     =head1 LICENSE
1455 wakaba 1.1
1456 wakaba 1.4 Copyright 2007 Wakaba <w@suika.fam.cx>
1457 wakaba 1.1
1458 wakaba 1.4 This program is free software; you can redistribute it and/or
1459     modify it under the same terms as Perl itself.
1460 wakaba 1.1
1461 wakaba 1.4 =cut
1462 wakaba 1.1
1463     1;
1464 wakaba 1.19 ## $Date: 2007/12/22 06:29:32 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24