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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.25 - (show annotations) (download)
Sun Nov 18 11:08:41 2007 UTC (17 years ago) by wakaba
Branch: MAIN
Changes since 1.24: +10 -2 lines
++ manakai/lib/Message/ChangeLog	18 Nov 2007 05:58:46 -0000
2007-11-18  Wakaba  <wakaba@suika.fam.cx>

	* Charset/: New directory.

++ manakai/lib/Message/DOM/ChangeLog	18 Nov 2007 08:56:34 -0000
2007-11-18  Wakaba  <wakaba@suika.fam.cx>

	* Document.pm, Entity.pm (manakai_has_bom,
	manakai_charset): New attributes.

++ manakai/lib/Message/Charset/ChangeLog	18 Nov 2007 11:08:08 -0000
2007-11-18  Wakaba  <wakaba@suika.fam.cx>

	* Info.pm: New Perl module.

2007-11-18  Wakaba  <wakaba@suika.fam.cx>

	* ChangeLog: New file.


++ manakai/t/ChangeLog	18 Nov 2007 08:41:50 -0000
2007-11-18  Wakaba  <wakaba@suika.fam.cx>

	* DOM-Document.t, DOM-Entity.t: New tests for |manakai_has_bom|
	attribute.

1 ## NOTE: This module will be renamed as Document.pm.
2
3 package Message::DOM::Document;
4 use strict;
5 our $VERSION=do{my @r=(q$Revision: 1.24 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
6 push our @ISA, 'Message::DOM::Node', 'Message::IF::Document',
7 'Message::IF::DocumentTraversal', 'Message::IF::DocumentXDoctype',
8 'Message::IF::DocumentSelector', # MUST in Selectors API spec
9 'Message::IF::HTMLDocument';
10 require Message::DOM::Node;
11 use Char::Class::XML
12 qw/
13 InXML_NameStartChar10 InXMLNameStartChar11
14 InXMLNameChar10 InXMLNameChar11
15 InXML_NCNameStartChar10 InXMLNCNameStartChar11
16 InXMLNCNameChar10 InXMLNCNameChar11
17 /;
18
19 sub ____new ($$) {
20 my $self = shift->SUPER::____new (undef);
21 $$self->{implementation} = $_[0];
22 $$self->{strict_error_checking} = 1;
23 $$self->{child_nodes} = [];
24 $$self->{'http://suika.fam.cx/www/2006/dom-config/strict-document-children'} = 1;
25 $$self->{'http://suika.fam.cx/www/2006/dom-config/dtd-default-attribute'} = 1;
26 $$self->{'http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree'} = 1;
27 $$self->{'error-handler'} = sub ($) {
28 ## NOTE: Same as one set by |setParameter| with |undef| value.
29 warn $_[0];
30 return $_[0]->severity != 3; # SEVERITY_FATAL_ERROR
31 };
32 return $self;
33 } # ____new
34
35 sub AUTOLOAD {
36 my $method_name = our $AUTOLOAD;
37 $method_name =~ s/.*:://;
38 return if $method_name eq 'DESTROY';
39
40 if ({
41 ## Read-only attributes (trivial accessors)
42 implementation => 1,
43 }->{$method_name}) {
44 no strict 'refs';
45 eval qq{
46 sub $method_name (\$) {
47 return \${\$_[0]}->{$method_name};
48 }
49 };
50 goto &{ $AUTOLOAD };
51 } elsif ({
52 ## Read-write attributes (DOMString, trivial accessors)
53 manakai_charset => 1,
54 document_uri => 1,
55 input_encoding => 1,
56 }->{$method_name}) {
57 no strict 'refs';
58 eval qq{
59 sub $method_name (\$;\$) {
60 if (\@_ > 1) {
61 if (\${\$_[0]}->{strict_error_checking} and
62 \${\$_[0]}->{manakai_read_only}) {
63 report Message::DOM::DOMException
64 -object => \$_[0],
65 -type => 'NO_MODIFICATION_ALLOWED_ERR',
66 -subtype => 'READ_ONLY_NODE_ERR';
67 }
68 if (defined \$_[1]) {
69 \${\$_[0]}->{$method_name} = ''.\$_[1];
70 } else {
71 delete \${\$_[0]}->{$method_name};
72 }
73 }
74 return \${\$_[0]}->{$method_name};
75 }
76 };
77 goto &{ $AUTOLOAD };
78 } elsif ({
79 ## Read-write attributes (boolean, trivial accessors)
80 all_declarations_processed => 1,
81 manakai_has_bom => 1,
82 }->{$method_name}) {
83 no strict 'refs';
84 eval qq{
85 sub $method_name (\$;\$) {
86 if (\@_ > 1) {
87 if (\${\$_[0]}->{manakai_strict_error_checking} and
88 \${\$_[0]}->{manakai_read_only}) {
89 report Message::DOM::DOMException
90 -object => \$_[0],
91 -type => 'NO_MODIFICATION_ALLOWED_ERR',
92 -subtype => 'READ_ONLY_NODE_ERR';
93 }
94 if (\$_[1]) {
95 \${\$_[0]}->{$method_name} = 1;
96 } else {
97 delete \${\$_[0]}->{$method_name};
98 }
99 }
100 return \${\$_[0]}->{$method_name};
101 }
102 };
103 goto &{ $AUTOLOAD };
104 } elsif (my $module_name = {
105 can_dispatch => 'Message::DOM::EventTargetNode',
106 create_attribute => 'Message::DOM::Attr',
107 create_attribute_ns => 'Message::DOM::Attr',
108 create_attribute_definition => 'Message::DOM::AttributeDefinition',
109 create_cdata_section => 'Message::DOM::Text',
110 create_comment => 'Message::DOM::CharacterData',
111 create_document_fragment => 'Message::DOM::DocumentFragment',
112 create_document_type_definition => 'Message::DOM::DocumentType',
113 create_element => 'Message::DOM::Element',
114 create_element_ns => 'Message::DOM::Element',
115 create_element_type_definition => 'Message::DOM::ElementTypeDefinition',
116 create_entity_reference => 'Message::DOM::EntityReference',
117 create_event => 'Message::DOM::EventTargetNode',
118 create_general_entity => 'Message::DOM::Entity',
119 create_notation => 'Message::DOM::Notation',
120 create_processing_instruction => 'Message::DOM::ProcessingInstruction',
121 manakai_create_serial_walker => 'Message::DOM::SerialWalker',
122 create_text_node => 'Message::DOM::Text',
123 create_tree_walker => 'Message::DOM::TreeWalker',
124 query_selector => 'Message::DOM::SelectorsAPI',
125 query_selector_all => 'Message::DOM::SelectorsAPI',
126 }->{$method_name}) {
127 eval qq{ require $module_name } or die $@;
128 goto &{ $AUTOLOAD };
129 } else {
130 require Carp;
131 Carp::croak (qq<Can't locate method "$AUTOLOAD">);
132 }
133 } # AUTOLOAD
134 sub implementation ($);
135 sub create_attribute ($$);
136 sub create_attribute_ns ($$$);
137 sub create_attribute_definition ($$);
138 sub create_cdata_section ($$);
139 sub create_comment ($$);
140 sub create_document_fragment ($);
141 sub create_document_type_definition ($$);
142 sub create_element ($$);
143 sub create_element_ns ($$$);
144 sub create_element_type_definition ($$);
145 sub create_entity_reference ($$);
146 sub create_general_entity ($$);
147 sub create_notation ($$);
148 sub create_processing_instruction ($$$);
149 sub create_text_node ($$);
150
151 ## |Node| attributes
152
153 sub base_uri ($) {
154 my $v = ${$_[0]}->{manakai_entity_base_uri};
155 if (defined $v) {
156 return $v;
157 } else {
158 return ${$_[0]}->{document_uri};
159 }
160 ## TODO: HTML5 <base>
161 } # base_uri
162
163 sub node_name () { '#document' }
164
165 sub node_type () { 9 } # DOCUMENT_NODE
166
167 sub text_content ($;$) {
168 my $self = shift;
169 if ($$self->{'http://suika.fam.cx/www/2006/dom-config/strict-document-children'}) {
170 return undef;
171 } else {
172 local $Error::Depth = $Error::Depth + 1;
173 return $self->SUPER::text_content (@_);
174 }
175 } # text_content
176
177 ## |Node| methods
178
179 sub adopt_node ($$) {
180 my ($self, $source) = @_;
181 ## TODO: Should we apply |copy-asis| configuration parameter to this method?
182
183 return undef unless UNIVERSAL::isa ($source, 'Message::DOM::Node');
184
185 my $strict = $self->strict_error_checking;
186 if ($strict and $$self->{manakai_read_only}) {
187 report Message::DOM::DOMException
188 -object => $self,
189 -type => 'NO_MODIFICATION_ALLOWED_ERR',
190 -subtype => 'READ_ONLY_NODE_ERR';
191 }
192
193 my $parent = $source->parent_node;
194 if ($strict and defined $parent and $$parent->{manakai_read_only}) {
195 report Message::DOM::DOMException
196 -object => $self,
197 -type => 'NO_MODIFICATION_ALLOWED_ERR',
198 -subtype => 'READ_ONLY_NODE_ERR';
199 }
200
201 my $nt = $source->node_type;
202 my $oe;
203 if ($nt == 2) { # ATTRIBUTE_NODE
204 $oe = $source->owner_element;
205 if ($strict and defined $oe and $$oe->{manakai_read_only}) {
206 report Message::DOM::DOMException
207 -object => $self,
208 -type => 'NO_MODIFICATION_ALLOWED_ERR',
209 -subtype => 'READ_ONLY_NODE_ERR';
210 }
211 } elsif ($nt == 9 or $nt == 10 or $nt == 6 or $nt == 12 or
212 $nt == 81001 or $nt == 81002) {
213 # DOCUMENT_NODE, DOCUMENT_TYPE_NODE, ENTITY_NODE, NOTATION_NODE,
214 # ELEMENT_TYPE_DEFINITION_NODE, ATTRIBUTE_DEFINITION_NODE
215 report Message::DOM::DOMException
216 -object => $self,
217 -type => 'NOT_SUPPORTED_ERR',
218 -subtype => 'ADOPT_NODE_TYPE_NOT_SUPPORTED_ERR';
219 ## ISSUE: Define ELEMENT_TYPE_DEFINITION_NODE and ATTRIBUTE_DEFINITION_NODE
220 }
221
222 my @change_od;
223 my @nodes = ($source);
224 while (@nodes) {
225 my $node = shift @nodes;
226 my $nt = $node->node_type;
227 if ($strict and $$node->{manakai_read_only}) {
228 report Message::DOM::DOMException
229 -object => $self,
230 -type => 'NO_MODIFICATION_ALLOWED_ERR',
231 -subtype => 'READ_ONLY_NODE_ERR';
232 }
233
234 push @change_od, $node;
235 push @nodes, @{$node->child_nodes}, @{$node->attributes or []};
236 } # @nodes
237
238 local $Error::Depth = $Error::Depth + 1;
239
240 if (defined $parent) {
241 $parent->remove_child ($source);
242 } elsif (defined $oe) {
243 $oe->remove_attribute_node ($source);
244 }
245
246 return $source if $self eq $change_od[0]->owner_document;
247 ## NOTE: The array must have more than zero
248 ## nodes by definition. In addition,
249 ## it cannot contain document or document
250 ## type nodes in current implementation.
251
252 my @ud_node;
253 for my $n (@change_od) {
254 $$n->{owner_document} = $self;
255 Scalar::Util::weaken ($$n->{owner_document});
256 if ($$n->{user_data}) {
257 push @ud_node, $n;
258 }
259 }
260
261 for my $src (@ud_node) {
262 my $src_ud = $$src->{user_data};
263 for my $key (keys %{$src_ud}) {
264 my $dh = $src_ud->{$key}->[1];
265 if ($dh) {
266 $dh->(5, $key, $src_ud->{$key}->[0], $src, undef); # NODE_ADOPTED
267 }
268 }
269 }
270
271 return $source;
272 } # adopt_node
273
274 sub append_child ($$) {
275 ## NOTE: Overrides |Node|'s implementation.
276 my $self = $_[0];
277
278 ## NOTE: |$self_od| code here in some $self->node_type.
279
280 ## -- Node Type check
281 my @new_child;
282 my $new_child_parent;
283 if ($_[1]->node_type == 11) { # DOCUMENT_FRAGMENT_NODE
284 push @new_child, @{$_[1]->child_nodes};
285 $new_child_parent = $_[1];
286 } else {
287 @new_child = ($_[1]);
288 $new_child_parent = $_[1]->parent_node;
289 }
290
291 ## NOTE: Depends on $self->node_type:
292 if ($$self->{strict_error_checking}) {
293 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
294 if ($self ne $child_od and $child_od->node_type != 10) {
295 report Message::DOM::DOMException # DOCUMENT_TYPE_NODE
296 -object => $self,
297 -type => 'WRONG_DOCUMENT_ERR',
298 -subtype => 'EXTERNAL_OBJECT_ERR';
299 }
300
301 if ($$self->{manakai_read_only} or
302 (@new_child and defined $new_child_parent and
303 $$new_child_parent->{manakai_read_only})) {
304 report Message::DOM::DOMException
305 -object => $self,
306 -type => 'NO_MODIFICATION_ALLOWED_ERR',
307 -subtype => 'READ_ONLY_NODE_ERR';
308 }
309
310 ## NOTE: Only in |Document|:
311 my $strict_children = $self->dom_config->get_parameter
312 (q<http://suika.fam.cx/www/2006/dom-config/strict-document-children>);
313 if ($strict_children) {
314 my $has_el;
315 my $has_dt;
316 my $child_nt = $_[1]->node_type;
317 if ($child_nt == 1) { # ELEMENT_NODE
318 $has_el = 1;
319 } elsif ($child_nt == 10) { # DOCUMENT_TYPE_NODE
320 $has_dt = 1;
321 } elsif ($child_nt == 11) { # DOCUMENT_FRAGMENT_NODE
322 for my $cn (@{$_[1]->child_nodes}) {
323 my $cnt = $cn->node_type;
324 if ($cnt == 1) { # ELEMENT_NODE
325 if ($has_el) {
326 report Message::DOM::DOMException
327 -object => $self,
328 -type => 'HIERARCHY_REQUEST_ERR',
329 -subtype => 'CHILD_NODE_TYPE_ERR';
330 }
331 $has_el = 1;
332 } elsif ($cnt == 10) { # DOCUMENT_TYPE_NODE
333 ## NOTE: |DocumentType| node cannot be contained in
334 ## |DocumentFragment| in strict mode.
335 if ($has_dt) {
336 report Message::DOM::DOMException
337 -object => $self,
338 -type => 'HIERARCHY_REQUEST_ERR',
339 -subtype => 'CHILD_NODE_TYPE_ERR';
340 }
341 $has_dt = 1;
342 }
343 }
344 }
345
346 if ($has_el) {
347 my $anode = $self->last_child;
348 while (defined $anode) {
349 if ($anode->node_type == 1) { # ELEMENT_NODE
350 report Message::DOM::DOMException
351 -object => $self,
352 -type => 'HIERARCHY_REQUEST_ERR',
353 -subtype => 'CHILD_NODE_TYPE_ERR';
354 }
355 $anode = $anode->previous_sibling;
356 }
357 } # has_el
358 if ($has_dt) {
359 my $anode = $self->last_child;
360 while (defined $anode) {
361 my $ant = $anode->node_type;
362 if ($ant == 1 or $ant == 10) { # ELEMENT_NODE or DOCUMENT_TYPE_NODE
363 report Message::DOM::DOMException
364 -object => $self,
365 -type => 'HIERARCHY_REQUEST_ERR',
366 -subtype => 'CHILD_NODE_TYPE_ERR';
367 }
368 $anode = $anode->previous_sibling;
369 }
370 } # has_dt
371 }
372
373 for my $cn (@new_child) {
374 unless ({
375 3, (not $strict_children), # TEXT_NODE
376 5, (not $strict_children), # ENTITY_REFERENCE_NODE
377 1, 1, # ELEMENT_NODE
378 4, (not $strict_children), # CDATA_SECTION_NODE
379 7, 1, # PROCESSING_INSTRUCTION_NODE
380 8, 1, # COMMENT_NODE
381 10, 1, # DOCUMENT_TYPE_NODE
382 }->{$cn->node_type}) {
383 report Message::DOM::DOMException
384 -object => $self,
385 -type => 'HIERARCHY_REQUEST_ERR',
386 -subtype => 'CHILD_NODE_TYPE_ERR';
387 }
388 }
389
390 ## NOTE: Ancestor check here in |Node|.
391 }
392
393 ## NOTE: "Insert at" code only in insert_before and replace_child
394
395 ## -- Removes from parent
396 if ($new_child_parent) {
397 if (@new_child == 1) {
398 my $v = $$new_child_parent->{child_nodes};
399 RP: for my $i (0..$#$v) {
400 if ($v->[$i] eq $new_child[0]) {
401 splice @$v, $i, 1, ();
402 last RP;
403 }
404 } # RP
405 } else {
406 @{$$new_child_parent->{child_nodes}} = ();
407 }
408 }
409
410 ## -- Rewrite the |parentNode| properties
411 for my $nc (@new_child) {
412 $$nc->{parent_node} = $self;
413 Scalar::Util::weaken ($$nc->{parent_node});
414 }
415
416 ## NOTE: Depends on method:
417 push @{$$self->{child_nodes}}, @new_child;
418
419 ## NOTE: Only in |Document|.
420 for (@new_child) {
421 delete $$_->{implementation};
422 $$_->{owner_document} = $self;
423 Scalar::Util::weaken ($$_->{owner_document});
424 }
425
426 return $_[1];
427 } # apepnd_child
428
429 sub manakai_append_text ($$) {
430 my $self = shift;
431 if ($$self->{'http://suika.fam.cx/www/2006/dom-config/strict-document-children'}) {
432 #
433 } else {
434 local $Error::Depth = $Error::Depth + 1;
435 return $self->SUPER::manakai_append_text (@_);
436 }
437 } # manakai_append_text
438
439 sub insert_before ($$) {
440 ## NOTE: Overrides |Node|'s implementation.
441 my $self = $_[0];
442
443 ## NOTE: |$self_od| code here depending on $self->node_type.
444
445 ## -- Node Type check
446 my @new_child;
447 my $new_child_parent;
448 if ($_[1]->node_type == 11) { # DOCUMENT_FRAGMENT_NODE
449 push @new_child, @{$_[1]->child_nodes};
450 $new_child_parent = $_[1];
451 } else {
452 @new_child = ($_[1]);
453 $new_child_parent = $_[1]->parent_node;
454 }
455
456 ## NOTE: Depends on $self->node_type:
457 if ($$self->{strict_error_checking}) {
458 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
459 if ($self ne $child_od and $child_od->node_type != 10) {
460 report Message::DOM::DOMException # DOCUMENT_TYPE_NODE
461 -object => $self,
462 -type => 'WRONG_DOCUMENT_ERR',
463 -subtype => 'EXTERNAL_OBJECT_ERR';
464 }
465
466 if ($$self->{manakai_read_only} or
467 (@new_child and defined $new_child_parent and
468 $$new_child_parent->{manakai_read_only})) {
469 report Message::DOM::DOMException
470 -object => $self,
471 -type => 'NO_MODIFICATION_ALLOWED_ERR',
472 -subtype => 'READ_ONLY_NODE_ERR';
473 }
474
475 ## NOTE: Only in |Document|:
476 my $strict_children = $self->dom_config->get_parameter
477 (q<http://suika.fam.cx/www/2006/dom-config/strict-document-children>);
478 if ($strict_children) {
479 my $has_el;
480 my $has_dt;
481 my $child_nt = $_[1]->node_type;
482 if ($child_nt == 1) { # ELEMENT_NODE
483 $has_el = 1;
484 } elsif ($child_nt == 10) { # DOCUMENT_TYPE_NODE
485 $has_dt = 1;
486 } elsif ($child_nt == 11) { # DOCUMENT_FRAGMENT_NODE
487 for my $cn (@{$_[1]->child_nodes}) {
488 my $cnt = $cn->node_type;
489 if ($cnt == 1) { # ELEMENT_NODE
490 if ($has_el) {
491 report Message::DOM::DOMException
492 -object => $self,
493 -type => 'HIERARCHY_REQUEST_ERR',
494 -subtype => 'CHILD_NODE_TYPE_ERR';
495 }
496 $has_el = 1;
497 } elsif ($cnt == 10) { # DOCUMENT_TYPE_NODE
498 ## NOTE: |DocumentType| node cannot be contained in
499 ## |DocumentFragment| in strict mode.
500 if ($has_dt) {
501 report Message::DOM::DOMException
502 -object => $self,
503 -type => 'HIERARCHY_REQUEST_ERR',
504 -subtype => 'CHILD_NODE_TYPE_ERR';
505 }
506 $has_dt = 1;
507 }
508 }
509 }
510
511 ## ISSUE: This code is wrong. Old manakai's implementation
512 ## is better, but it is also wrong in some edge cases.
513 ## Maybe we should remove these code entirely. DOM3Core
514 ## conformance is not important for this bit. It only makes
515 ## things too complex. Same for replace_child's code.
516 if ($has_el) {
517 my $anode = $self->last_child;
518 while (defined $anode) {
519 if ($anode->node_type == 1) { # ELEMENT_NODE
520 report Message::DOM::DOMException
521 -object => $self,
522 -type => 'HIERARCHY_REQUEST_ERR',
523 -subtype => 'CHILD_NODE_TYPE_ERR';
524 }
525 $anode = $anode->previous_sibling;
526 }
527 } # has_el
528 if ($has_dt) {
529 my $anode = $self->last_child;
530 while (defined $anode) {
531 my $ant = $anode->node_type;
532 if ($ant == 1 or $ant == 10) { # ELEMENT_NODE or DOCUMENT_TYPE_NODE
533 report Message::DOM::DOMException
534 -object => $self,
535 -type => 'HIERARCHY_REQUEST_ERR',
536 -subtype => 'CHILD_NODE_TYPE_ERR';
537 }
538 $anode = $anode->previous_sibling;
539 }
540 } # has_dt
541 }
542
543 for my $cn (@new_child) {
544 unless ({
545 3, (not $strict_children), # TEXT_NODE
546 5, (not $strict_children), # ENTITY_REFERENCE_NODE
547 1, 1, # ELEMENT_NODE
548 4, (not $strict_children), # CDATA_SECTION_NODE
549 7, 1, # PROCESSING_INSTRUCTION_NODE
550 8, 1, # COMMENT_NODE
551 10, 1, # DOCUMENT_TYPE_NODE
552 }->{$cn->node_type}) {
553 report Message::DOM::DOMException
554 -object => $self,
555 -type => 'HIERARCHY_REQUEST_ERR',
556 -subtype => 'CHILD_NODE_TYPE_ERR';
557 }
558 }
559
560 ## NOTE: Ancestor check here in |Node|.
561 }
562
563 ## -- Insert at... ## NOTE: Only in insert_before and replace_child
564 my $index = -1; # last
565 if (defined $_[2]) {
566 ## error if $_[1] eq $_[2];
567
568 my $cns = $self->child_nodes;
569 my $cnsl = @$cns;
570 C: {
571 $index = 0;
572 for my $i (0..($cnsl-1)) {
573 my $cn = $cns->[$i];
574 if ($cn eq $_[2]) {
575 $index += $i;
576 last C;
577 } elsif ($cn eq $_[1]) {
578 $index = -1; # offset
579 }
580 }
581
582 report Message::DOM::DOMException
583 -object => $self,
584 -type => 'NOT_FOUND_ERR',
585 -subtype => 'NOT_CHILD_ERR';
586 } # C
587 }
588 ## NOTE: "else" only in replace_child
589
590 ## -- Removes from parent
591 if ($new_child_parent) {
592 if (@new_child == 1) {
593 my $v = $$new_child_parent->{child_nodes};
594 RP: for my $i (0..$#$v) {
595 if ($v->[$i] eq $new_child[0]) {
596 splice @$v, $i, 1, ();
597 last RP;
598 }
599 } # RP
600 } else {
601 @{$$new_child_parent->{child_nodes}} = ();
602 }
603 }
604
605 ## -- Rewrite the |parentNode| properties
606 for my $nc (@new_child) {
607 $$nc->{parent_node} = $self;
608 Scalar::Util::weaken ($$nc->{parent_node});
609 }
610
611 ## NOTE: Depends on method:
612 if ($index == -1) {
613 push @{$$self->{child_nodes}}, @new_child;
614 } else {
615 splice @{$$self->{child_nodes}}, $index, 0, @new_child;
616 }
617
618 ## NOTE: Only in |Document|.
619 for (@new_child) {
620 delete $$_->{implementation};
621 $$_->{owner_document} = $self;
622 Scalar::Util::weaken ($$_->{owner_document});
623 }
624
625 return $_[1];
626 } # insert_before
627
628 sub replace_child ($$) {
629 ## NOTE: Overrides |Node|'s implementation.
630 my $self = $_[0];
631
632 ## NOTE: |$self_od| code here depending on $self->node_type.
633
634 ## -- Node Type check
635 my @new_child;
636 my $new_child_parent;
637 if ($_[1]->node_type == 11) { # DOCUMENT_FRAGMENT_NODE
638 push @new_child, @{$_[1]->child_nodes};
639 $new_child_parent = $_[1];
640 } else {
641 @new_child = ($_[1]);
642 $new_child_parent = $_[1]->parent_node;
643 }
644
645 ## NOTE: Depends on $self->node_type:
646 if ($$self->{strict_error_checking}) {
647 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
648 if ($self ne $child_od and $child_od->node_type != 10) {
649 report Message::DOM::DOMException # DOCUMENT_TYPE_NODE
650 -object => $self,
651 -type => 'WRONG_DOCUMENT_ERR',
652 -subtype => 'EXTERNAL_OBJECT_ERR';
653 }
654
655 if ($$self->{manakai_read_only} or
656 (@new_child and defined $new_child_parent and
657 $$new_child_parent->{manakai_read_only})) {
658 report Message::DOM::DOMException
659 -object => $self,
660 -type => 'NO_MODIFICATION_ALLOWED_ERR',
661 -subtype => 'READ_ONLY_NODE_ERR';
662 }
663
664 ## NOTE: Only in |Document|:
665 my $strict_children = $self->dom_config->get_parameter
666 (q<http://suika.fam.cx/www/2006/dom-config/strict-document-children>);
667 if ($strict_children) {
668 my $has_el;
669 my $has_dt;
670 my $child_nt = $_[1]->node_type;
671 if ($child_nt == 1) { # ELEMENT_NODE
672 $has_el = 1;
673 } elsif ($child_nt == 10) { # DOCUMENT_TYPE_NODE
674 $has_dt = 1;
675 } elsif ($child_nt == 11) { # DOCUMENT_FRAGMENT_NODE
676 for my $cn (@{$_[1]->child_nodes}) {
677 my $cnt = $cn->node_type;
678 if ($cnt == 1) { # ELEMENT_NODE
679 if ($has_el) {
680 report Message::DOM::DOMException
681 -object => $self,
682 -type => 'HIERARCHY_REQUEST_ERR',
683 -subtype => 'CHILD_NODE_TYPE_ERR';
684 }
685 $has_el = 1;
686 } elsif ($cnt == 10) { # DOCUMENT_TYPE_NODE
687 ## NOTE: |DocumentType| node cannot be contained in
688 ## |DocumentFragment| in strict mode.
689 if ($has_dt) {
690 report Message::DOM::DOMException
691 -object => $self,
692 -type => 'HIERARCHY_REQUEST_ERR',
693 -subtype => 'CHILD_NODE_TYPE_ERR';
694 }
695 $has_dt = 1;
696 }
697 }
698 }
699
700 if ($has_el) {
701 my $anode = $self->last_child;
702 while (defined $anode) {
703 if ($anode->node_type == 1) { # ELEMENT_NODE
704 report Message::DOM::DOMException
705 -object => $self,
706 -type => 'HIERARCHY_REQUEST_ERR',
707 -subtype => 'CHILD_NODE_TYPE_ERR';
708 }
709 $anode = $anode->previous_sibling;
710 }
711 } # has_el
712 if ($has_dt) {
713 my $anode = $self->last_child;
714 while (defined $anode) {
715 my $ant = $anode->node_type;
716 if ($ant == 1 or $ant == 10) { # ELEMENT_NODE or DOCUMENT_TYPE_NODE
717 report Message::DOM::DOMException
718 -object => $self,
719 -type => 'HIERARCHY_REQUEST_ERR',
720 -subtype => 'CHILD_NODE_TYPE_ERR';
721 }
722 $anode = $anode->previous_sibling;
723 }
724 } # has_dt
725 }
726
727 for my $cn (@new_child) {
728 unless ({
729 3, (not $strict_children), # TEXT_NODE
730 5, (not $strict_children), # ENTITY_REFERENCE_NODE
731 1, 1, # ELEMENT_NODE
732 4, (not $strict_children), # CDATA_SECTION_NODE
733 7, 1, # PROCESSING_INSTRUCTION_NODE
734 8, 1, # COMMENT_NODE
735 10, 1, # DOCUMENT_TYPE_NODE
736 }->{$cn->node_type}) {
737 report Message::DOM::DOMException
738 -object => $self,
739 -type => 'HIERARCHY_REQUEST_ERR',
740 -subtype => 'CHILD_NODE_TYPE_ERR';
741 }
742 }
743
744 ## NOTE: Ancestor check here in |Node|.
745 }
746
747 ## -- Insert at... ## NOTE: Only in insertBefore and replaceChild
748 my $index = -1; # last
749 if (defined $_[2]) {
750 ## error if $_[1] eq $_[2];
751
752 my $cns = $self->child_nodes;
753 my $cnsl = @$cns;
754 C: {
755 $index = 0;
756 for my $i (0..($cnsl-1)) {
757 my $cn = $cns->[$i];
758 if ($cn eq $_[2]) {
759 $index += $i;
760 last C;
761 } elsif ($cn eq $_[1]) {
762 $index = -1; # offset
763 }
764 }
765
766 report Message::DOM::DOMException
767 -object => $self,
768 -type => 'NOT_FOUND_ERR',
769 -subtype => 'NOT_CHILD_ERR';
770 } # C
771 } else {
772 ## NOTE: Only in replaceChild
773 report Message::DOM::DOMException
774 -object => $self,
775 -type => 'NOT_FOUND_ERR',
776 -subtype => 'NOT_CHILD_ERR';
777 }
778
779 ## -- Removes from parent
780 if ($new_child_parent) {
781 if (@new_child == 1) {
782 my $v = $$new_child_parent->{child_nodes};
783 RP: for my $i (0..$#$v) {
784 if ($v->[$i] eq $new_child[0]) {
785 splice @$v, $i, 1, ();
786 last RP;
787 }
788 } # RP
789 } else {
790 @{$$new_child_parent->{child_nodes}} = ();
791 }
792 }
793
794 ## -- Rewrite the |parentNode| properties
795 for my $nc (@new_child) {
796 $$nc->{parent_node} = $self;
797 Scalar::Util::weaken ($$nc->{parent_node});
798 }
799
800 ## NOTE: Depends on method:
801 splice @{$$self->{child_nodes}}, $index, 1, @new_child;
802 delete ${$_[2]}->{parent_node};
803
804 ## NOTE: Only in |Document|.
805 for (@new_child) {
806 delete $$_->{implementation};
807 $$_->{owner_document} = $self;
808 Scalar::Util::weaken ($$_->{owner_document});
809 }
810
811 return $_[2];
812 } # replace_child
813
814 ## |Document| attributes
815
816 ## NOTE: A manakai extension.
817 sub all_declarations_processed ($;$);
818
819 ## TODO: documentation
820 sub manakai_charset ($;$);
821
822 sub doctype ($) {
823 my $self = $_[0];
824 for (@{$self->child_nodes}) {
825 if ($_->node_type == 10) { # DOCUMENT_TYPE_NODE
826 return $_;
827 }
828 }
829 return undef;
830 } # doctype
831
832 sub document_element ($) {
833 my $self = shift;
834 for (@{$self->child_nodes}) {
835 if ($_->node_type == 1) { # ELEMENT_NODE
836 return $_;
837 }
838 }
839 return undef;
840 } # document_element
841
842 sub document_uri ($;$);
843
844 sub dom_config ($) {
845 require Message::DOM::DOMConfiguration;
846 return bless \\($_[0]), 'Message::DOM::DOMConfiguration';
847 } # dom_config
848
849 sub manakai_entity_base_uri ($;$) {
850 my $self = $_[0];
851 if (@_ > 1) {
852 if ($$self->{strict_error_checking}) {
853 if ($$self->{manakai_read_only}) {
854 report Message::DOM::DOMException
855 -object => $self,
856 -type => 'NO_MODIFICATION_ALLOWED_ERR',
857 -subtype => 'READ_ONLY_NODE_ERR';
858 }
859 }
860 if (defined $_[1]) {
861 $$self->{manakai_entity_base_uri} = ''.$_[1];
862 } else {
863 delete $$self->{manakai_entity_base_uri};
864 }
865 }
866
867 if (defined $$self->{manakai_entity_base_uri}) {
868 return $$self->{manakai_entity_base_uri};
869 } else {
870 return $$self->{document_uri};
871 }
872 } # manakai_entity_base_uri
873
874 ## TODO: documentation
875 sub manakai_has_bom ($;$);
876
877 sub input_encoding ($;$);
878
879 sub strict_error_checking ($;$) {
880 ## NOTE: Same as trivial boolean accessor, except no read-only checking.
881 if (@_ > 1) {
882 if ($_[1]) {
883 ${$_[0]}->{strict_error_checking} = 1;
884 } else {
885 delete ${$_[0]}->{strict_error_checking};
886 }
887 }
888 return ${$_[0]}->{strict_error_checking};
889 } # strict_error_checking
890
891 ## ISSUE: Setting manakai_is_html true shadows
892 ## xml_* properties. Is this desired?
893
894 sub xml_encoding ($;$) {
895 my $self = $_[0];
896 if (@_ > 1) {
897 ## NOTE: A manakai extension.
898 if ($$self->{strict_error_checking}) {
899 if ($$self->{manakai_is_html}) {
900 report Message::DOM::DOMException
901 -object => $self,
902 -type => 'NOT_SUPPORTED_ERR',
903 -subtype => 'NON_HTML_OPERATION_ERR';
904 }
905 if ($$self->{manakai_read_only}) {
906 report Message::DOM::DOMException
907 -object => $self,
908 -type => 'NO_MODIFICATION_ALLOWED_ERR',
909 -subtype => 'READ_ONLY_NODE_ERR';
910 }
911 }
912 if (defined $_[1]) {
913 $$self->{xml_encoding} = ''.$_[1];
914 } else {
915 delete $$self->{xml_encoding};
916 }
917 }
918
919 if ($$self->{manakai_is_html}) {
920 return undef;
921 } else {
922 return $$self->{xml_encoding};
923 }
924 } # xml_encoding
925
926 sub xml_standalone ($;$) {
927 my $self = $_[0];
928 if (@_ > 1) {
929 if ($$self->{strict_error_checking}) {
930 if ($$self->{manakai_is_html}) {
931 report Message::DOM::DOMException
932 -object => $self,
933 -type => 'NOT_SUPPORTED_ERR',
934 -subtype => 'NON_HTML_OPERATION_ERR';
935 }
936 ## NOTE: Not in DOM3.
937 if ($$self->{manakai_read_only}) {
938 report Message::DOM::DOMException
939 -object => $self,
940 -type => 'NO_MODIFICATION_ALLOWED_ERR',
941 -subtype => 'READ_ONLY_NODE_ERR';
942 }
943 }
944 if ($_[1]) {
945 $$self->{xml_standalone} = 1;
946 } else {
947 delete $$self->{xml_standalone};
948 }
949 }
950
951 if ($$self->{manakai_is_html}) {
952 return 0;
953 } else {
954 return $$self->{xml_standalone};
955 }
956 } # xml_standalone
957
958 sub xml_version ($;$) {
959 my $self = $_[0];
960 if (@_ > 1) {
961 my $v = ''.$_[1];
962 if ($$self->{strict_error_checking}) {
963 if ($$self->{manakai_is_html}) {
964 report Message::DOM::DOMException
965 -object => $self,
966 -type => 'NOT_SUPPORTED_ERR',
967 -subtype => 'NON_HTML_OPERATION_ERR';
968 }
969 if ($v ne '1.0' and $v ne '1.1') {
970 report Message::DOM::DOMException
971 -object => $self,
972 -type => 'NOT_SUPPORTED_ERR',
973 -subtype => 'UNKNOWN_XML_VERSION_ERR';
974 }
975 if ($$self->{manakai_read_only}) {
976 ## ISSUE: Not in DOM3.
977 report Message::DOM::DOMException
978 -object => $self,
979 -type => 'NO_MODIFICATION_ALLOWED_ERR',
980 -subtype => 'READ_ONLY_NODE_ERR';
981 }
982 }
983 $$self->{xml_version} = $v;
984 }
985
986 if (defined wantarray) {
987 if ($$self->{manakai_is_html}) {
988 return undef;
989 } elsif (defined $$self->{xml_version}) {
990 return $$self->{xml_version};
991 } else {
992 return '1.0';
993 }
994 }
995 } # xml_version
996
997 ## |Document| methods
998
999 sub get_element_by_id ($$) {
1000 local $Error::Depth = $Error::Depth + 1;
1001 my @nodes = @{$_[0]->child_nodes};
1002 N: while (@nodes) {
1003 my $node = shift @nodes;
1004 next N unless $node->node_type == 1; # ELEMENT_NODE
1005 for my $attr (@{$node->attributes}) {
1006 if ($attr->is_id and $attr->value eq $_[1]) {
1007 return $node;
1008 }
1009 }
1010 unshift @nodes, @{$node->child_nodes};
1011 } # N
1012 return undef;
1013 } # get_element_by_id
1014
1015 ## TODO: HTML5 case normalization
1016 sub get_elements_by_tag_name ($$) {
1017 my $name = ''.$_[1];
1018 my $chk;
1019 if ($name eq '*') {
1020 $chk = sub () { 1 };
1021 } else {
1022 $chk = sub ($) {
1023 return $_[0]->manakai_tag_name eq $name;
1024 };
1025 }
1026
1027 require Message::DOM::NodeList;
1028 return bless \[$_[0], $chk], 'Message::DOM::NodeList::GetElementsList';
1029 } # get_elements_by_tag_name
1030
1031 sub get_elements_by_tag_name_ns ($$$) {
1032 my $nsuri = defined $_[1] ? ''.$_[1] : '';
1033 my $lname = ''.$_[2];
1034 my $chk;
1035 if ($nsuri eq '*') {
1036 if ($lname eq '*') {
1037 $chk = sub () { 1 };
1038 } else {
1039 $chk = sub ($) {
1040 return $_[0]->manakai_local_name eq $lname;
1041 };
1042 }
1043 } elsif ($nsuri eq '') {
1044 if ($lname eq '*') {
1045 $chk = sub ($) {
1046 return not defined $_[0]->namespace_uri;
1047 };
1048 } else {
1049 $chk = sub ($) {
1050 return (not defined $_[0]->namespace_uri and
1051 $_[0]->manakai_local_name eq $lname);
1052 };
1053 }
1054 } else {
1055 if ($lname eq '*') {
1056 $chk = sub ($) {
1057 my $ns = $_[0]->namespace_uri;
1058 return (defined $ns and $ns eq $nsuri);
1059 };
1060 } else {
1061 $chk = sub ($) {
1062 my $ns = $_[0]->namespace_uri;
1063 return (defined $ns and $ns eq $nsuri and
1064 $_[0]->manakai_local_name eq $lname);
1065 };
1066 }
1067 }
1068
1069 require Message::DOM::NodeList;
1070 return bless \[$_[0], $chk], 'Message::DOM::NodeList::GetElementsList';
1071 } # get_elements_by_tag_name
1072
1073 ## TODO: import_node
1074
1075 ## TODO: normalize_document
1076
1077 ## TODO: rename_node
1078
1079 ## |DocumentTraversal| methods
1080
1081 ## TODO: create_node_iterator
1082
1083 sub manakai_create_serial_walker ($$;$$$);
1084
1085 sub create_tree_walker ($$;$$$);
1086
1087 ## |HTMLDocument| attributes
1088
1089 sub compat_mode ($) {
1090 if (${$_[0]}->{manakai_is_html}) {
1091 if (defined ${$_[0]}->{manakai_compat_mode} and
1092 ${$_[0]}->{manakai_compat_mode} eq 'quirks') {
1093 return 'BackCompat';
1094 }
1095 }
1096 return 'CSS1Compat';
1097 } # compat_mode
1098
1099 sub manakai_compat_mode ($;$) {
1100 if (${$_[0]}->{manakai_is_html}) {
1101 if (@_ > 1 and defined $_[1] and
1102 {'no quirks' => 1, 'limited quirks' => 1, 'quirks' => 1}->{$_[1]}) {
1103 ${$_[0]}->{manakai_compat_mode} = $_[1];
1104 }
1105 return ${$_[0]}->{manakai_compat_mode} || 'no quirks';
1106 } else {
1107 return 'no quirks';
1108 }
1109 } # manakai_compat_mode
1110
1111 sub inner_html ($;$) {
1112 my $self = $_[0];
1113 local $Error::Depth = $Error::Depth + 1;
1114
1115 if ($$self->{manakai_is_html}) {
1116 require Whatpm::HTML;
1117 if (@_ > 1) {
1118 ## Step 1
1119 ## TODO: Stop parsing and ...
1120
1121 ## Step 2
1122 my @cn = @{$self->child_nodes};
1123 for (@cn) { ## NOTE: Might throw a |NO_MODIFICATION_ALLOWED_ERR|.
1124 $self->remove_child ($_); #
1125 }
1126
1127 ## Step 3, 4, 5
1128 Whatpm::HTML->parse_string ($_[1] => $self);
1129
1130 ## TODO:
1131 ## <script>var input = function_to_do_xmlhttprequest (location.href);
1132 ## document.innerHTML = input</script>
1133
1134 return unless defined wantarray;
1135 }
1136
1137 require Whatpm::HTML::Serializer;
1138 return ${ Whatpm::HTML::Serializer->get_inner_html ($self) };
1139 } else {
1140 if (@_ > 1) {
1141 ## Step 1
1142 require Whatpm::XMLParser; # MUST
1143 my $doc = $self->implementation->create_document;
1144
1145 ## Step 2
1146 #
1147
1148 ## Step 3
1149 $doc = Whatpm::XMLParser->parse_string ($_[1] => $doc); # MUST
1150
1151 ## Step 4
1152 #
1153
1154 ## Step 5
1155 ## TODO: ill-formed -> SYNTAX_ERR # MUST
1156
1157 ## Step 6 # MUST
1158 my @cn = @{$self->child_nodes}; ## TODO: If read-only
1159 for (@cn) {
1160 $self->remove_child ($_);
1161 }
1162
1163 ## Step 7, 8, 9, 10
1164 for my $node (@{$doc->child_nodes}) {
1165 $self->append_child ($self->adopt_node ($node));
1166 }
1167
1168 return unless defined wantarray;
1169 }
1170
1171 ## TODO: This serializer is currently not conformant to HTML5 spec.
1172 require Whatpm::XMLSerializer;
1173 my $r = '';
1174 for (@{$self->child_nodes}) {
1175 $r .= ${ Whatpm::XMLSerializer->get_outer_xml ($_, sub {
1176 ## TODO: INVALID_STATE_ERR
1177 }) };
1178 }
1179 return $r;
1180 }
1181 } # inner_html
1182
1183 sub manakai_is_html ($;$) {
1184 if (@_ > 1) {
1185 if ($_[1]) {
1186 ${$_[0]}->{manakai_is_html} = 1;
1187 } else {
1188 delete ${$_[0]}->{manakai_is_html};
1189 delete ${$_[0]}->{manakai_compat_mode};
1190 }
1191 }
1192 return ${$_[0]}->{manakai_is_html};
1193 } # manakai_is_html
1194
1195 package Message::IF::Document;
1196 package Message::IF::DocumentTraversal;
1197 package Message::IF::DocumentXDoctype;
1198 package Message::IF::DocumentSelector;
1199 package Message::IF::HTMLDocument;
1200
1201 package Message::DOM::DOMImplementation;
1202
1203 sub create_document ($;$$$) {
1204 my $r = Message::DOM::Document->____new ($_[0]);
1205
1206 if (defined $_[2]) {
1207 local $Error::Depth = $Error::Depth + 1;
1208 $r->append_child ($r->create_element_ns ($_[1], $_[2])); # NAMESPACE_ERR
1209 ## NOTE: manakai might raise DOMExceptions in cases not defined
1210 ## in DOM3Core spec: XMLNSPREFIX_NONXMLNSNS_ERR,
1211 ## XMLNS_NONXMLNSNS_ERR, and NONXMLNSPREFIX_XMLNSNS_ERR.
1212 } elsif (defined $_[1]) {
1213 report Message::DOM::DOMException
1214 -object => $_[0],
1215 -type => 'NAMESPACE_ERR',
1216 -subtype => 'QNAME_NULLNS_ERR';
1217 }
1218
1219 if (defined $_[3]) {
1220 if ($_[3]->parent_node) {
1221 report Message::DOM::DOMException
1222 -object => $_[0],
1223 -type => 'WRONG_DOCUMENT_ERR',
1224 -subtype => 'INUSE_DOCTYPE_ERR';
1225 }
1226 local $Error::Depth = $Error::Depth + 1;
1227 $r->insert_before ($_[3], $r->first_child); # EXTERNAL_OBJECT_ERR
1228 }
1229
1230 return $r;
1231 } # create_document
1232
1233 =head1 LICENSE
1234
1235 Copyright 2007 Wakaba <w@suika.fam.cx>
1236
1237 This program is free software; you can redistribute it and/or
1238 modify it under the same terms as Perl itself.
1239
1240 =cut
1241
1242 1;
1243 ## $Date: 2007/11/11 04:23:32 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24