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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

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

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

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

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

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

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

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24